summaryrefslogtreecommitdiffstats
path: root/package
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /package
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'package')
-rw-r--r--package/CppunitTest_package2_test.mk40
-rw-r--r--package/IwyuFilter_package.yaml24
-rw-r--r--package/Library_package2.mk85
-rw-r--r--package/Library_xstor.mk45
-rw-r--r--package/Makefile7
-rw-r--r--package/Module_package.mk22
-rw-r--r--package/Package_dtd.mk14
-rw-r--r--package/README.md3
-rw-r--r--package/dtd/Manifest.dtd48
-rw-r--r--package/inc/ByteChucker.hxx90
-rw-r--r--package/inc/ByteGrabber.hxx76
-rw-r--r--package/inc/CRC32.hxx48
-rw-r--r--package/inc/EncryptedDataHeader.hxx53
-rw-r--r--package/inc/EncryptionData.hxx88
-rw-r--r--package/inc/HashMaps.hxx36
-rw-r--r--package/inc/PackageConstants.hxx67
-rw-r--r--package/inc/ThreadedDeflater.hxx71
-rw-r--r--package/inc/ZipEntry.hxx40
-rw-r--r--package/inc/ZipEnumeration.hxx35
-rw-r--r--package/inc/ZipFile.hxx173
-rw-r--r--package/inc/ZipOutputEntry.hxx161
-rw-r--r--package/inc/ZipOutputStream.hxx98
-rw-r--r--package/inc/ZipPackage.hxx177
-rw-r--r--package/inc/ZipPackageBuffer.hxx61
-rw-r--r--package/inc/ZipPackageEntry.hxx97
-rw-r--r--package/inc/ZipPackageFolder.hxx146
-rw-r--r--package/inc/ZipPackageStream.hxx172
-rw-r--r--package/inc/pch/precompiled_package2.cxx12
-rw-r--r--package/inc/pch/precompiled_package2.hxx178
-rw-r--r--package/inc/pch/precompiled_xstor.cxx12
-rw-r--r--package/inc/pch/precompiled_xstor.hxx111
-rw-r--r--package/inc/zipfileaccess.hxx88
-rw-r--r--package/qa/cppunit/data/a2z.zipbin0 -> 30774 bytes
-rw-r--r--package/qa/cppunit/data/export64.zipbin0 -> 3556 bytes
-rw-r--r--package/qa/cppunit/data/fail/.gitignore0
-rw-r--r--package/qa/cppunit/data/fail/EDB-29934-1.zipbin0 -> 2009 bytes
-rw-r--r--package/qa/cppunit/data/indeterminate/.gitignore0
-rw-r--r--package/qa/cppunit/data/pass/.gitignore0
-rw-r--r--package/qa/cppunit/data/pass/ofz56826-1.zipbin0 -> 155 bytes
-rw-r--r--package/qa/cppunit/test_package.cxx255
-rw-r--r--package/qa/ofopxmlstorages/StorageTest.java7
-rw-r--r--package/qa/ofopxmlstorages/StorageUnitTest.java149
-rw-r--r--package/qa/ofopxmlstorages/Test01.java218
-rw-r--r--package/qa/ofopxmlstorages/Test02.java182
-rw-r--r--package/qa/ofopxmlstorages/Test03.java251
-rw-r--r--package/qa/ofopxmlstorages/Test04.java326
-rw-r--r--package/qa/ofopxmlstorages/Test05.java332
-rw-r--r--package/qa/ofopxmlstorages/Test06.java295
-rw-r--r--package/qa/ofopxmlstorages/Test07.java276
-rw-r--r--package/qa/ofopxmlstorages/Test08.java279
-rw-r--r--package/qa/ofopxmlstorages/TestHelper.java1111
-rw-r--r--package/qa/ofopxmlstorages/makefile.mk82
-rw-r--r--package/qa/storages/BorderedStream.java213
-rw-r--r--package/qa/storages/RegressionTest_114358.java208
-rw-r--r--package/qa/storages/RegressionTest_125919.java152
-rw-r--r--package/qa/storages/RegressionTest_i26398.java164
-rw-r--r--package/qa/storages/RegressionTest_i27773.java317
-rw-r--r--package/qa/storages/RegressionTest_i29169.java387
-rw-r--r--package/qa/storages/RegressionTest_i29321.java188
-rw-r--r--package/qa/storages/RegressionTest_i30400.java453
-rw-r--r--package/qa/storages/RegressionTest_i30677.java281
-rw-r--r--package/qa/storages/RegressionTest_i35095.java184
-rw-r--r--package/qa/storages/RegressionTest_i46848.java209
-rw-r--r--package/qa/storages/RegressionTest_i49755.java290
-rw-r--r--package/qa/storages/RegressionTest_i55821.java129
-rw-r--r--package/qa/storages/RegressionTest_i59886.java261
-rw-r--r--package/qa/storages/RegressionTest_i61909.java185
-rw-r--r--package/qa/storages/RegressionTest_i84234.java152
-rw-r--r--package/qa/storages/StorageTest.java25
-rw-r--r--package/qa/storages/StorageUnitTest.java319
-rw-r--r--package/qa/storages/Test01.java195
-rw-r--r--package/qa/storages/Test02.java181
-rw-r--r--package/qa/storages/Test03.java249
-rw-r--r--package/qa/storages/Test04.java325
-rw-r--r--package/qa/storages/Test05.java317
-rw-r--r--package/qa/storages/Test06.java297
-rw-r--r--package/qa/storages/Test07.java180
-rw-r--r--package/qa/storages/Test08.java248
-rw-r--r--package/qa/storages/Test09.java156
-rw-r--r--package/qa/storages/Test10.java250
-rw-r--r--package/qa/storages/Test11.java236
-rw-r--r--package/qa/storages/Test12.java258
-rw-r--r--package/qa/storages/Test13.java233
-rw-r--r--package/qa/storages/Test14.java206
-rw-r--r--package/qa/storages/Test15.java286
-rw-r--r--package/qa/storages/Test16.java177
-rw-r--r--package/qa/storages/Test17.java160
-rw-r--r--package/qa/storages/Test18.java190
-rw-r--r--package/qa/storages/TestHelper.java1679
-rw-r--r--package/qa/storages/makefile.mk109
-rw-r--r--package/source/manifest/ManifestDefines.hxx102
-rw-r--r--package/source/manifest/ManifestExport.cxx558
-rw-r--r--package/source/manifest/ManifestExport.hxx38
-rw-r--r--package/source/manifest/ManifestImport.cxx599
-rw-r--r--package/source/manifest/ManifestImport.hxx103
-rw-r--r--package/source/manifest/ManifestReader.cxx104
-rw-r--r--package/source/manifest/ManifestReader.hxx54
-rw-r--r--package/source/manifest/ManifestWriter.cxx92
-rw-r--r--package/source/manifest/ManifestWriter.hxx54
-rw-r--r--package/source/xstor/disposelistener.cxx46
-rw-r--r--package/source/xstor/disposelistener.hxx44
-rw-r--r--package/source/xstor/ocompinstream.cxx597
-rw-r--r--package/source/xstor/ocompinstream.hxx109
-rw-r--r--package/source/xstor/ohierarchyholder.cxx326
-rw-r--r--package/source/xstor/ohierarchyholder.hxx116
-rw-r--r--package/source/xstor/oseekinstream.cxx144
-rw-r--r--package/source/xstor/oseekinstream.hxx60
-rw-r--r--package/source/xstor/owriteablestream.cxx3189
-rw-r--r--package/source/xstor/owriteablestream.hxx358
-rw-r--r--package/source/xstor/selfterminatefilestream.cxx95
-rw-r--r--package/source/xstor/selfterminatefilestream.hxx64
-rw-r--r--package/source/xstor/switchpersistencestream.cxx407
-rw-r--r--package/source/xstor/switchpersistencestream.hxx103
-rw-r--r--package/source/xstor/xfactory.cxx292
-rw-r--r--package/source/xstor/xfactory.hxx55
-rw-r--r--package/source/xstor/xstor.component27
-rw-r--r--package/source/xstor/xstorage.cxx5493
-rw-r--r--package/source/xstor/xstorage.hxx540
-rw-r--r--package/source/zipapi/ByteChucker.cxx53
-rw-r--r--package/source/zipapi/ByteGrabber.cxx122
-rw-r--r--package/source/zipapi/CRC32.cxx72
-rw-r--r--package/source/zipapi/Deflater.cxx162
-rw-r--r--package/source/zipapi/Inflater.cxx137
-rw-r--r--package/source/zipapi/MemoryByteGrabber.hxx114
-rw-r--r--package/source/zipapi/ThreadedDeflater.cxx220
-rw-r--r--package/source/zipapi/XBufferedThreadedStream.cxx189
-rw-r--r--package/source/zipapi/XBufferedThreadedStream.hxx84
-rw-r--r--package/source/zipapi/XUnbufferedStream.cxx331
-rw-r--r--package/source/zipapi/XUnbufferedStream.hxx93
-rw-r--r--package/source/zipapi/ZipEnumeration.cxx39
-rw-r--r--package/source/zipapi/ZipFile.cxx1474
-rw-r--r--package/source/zipapi/ZipOutputEntry.cxx404
-rw-r--r--package/source/zipapi/ZipOutputStream.cxx375
-rw-r--r--package/source/zipapi/blowfishcontext.cxx109
-rw-r--r--package/source/zipapi/blowfishcontext.hxx51
-rw-r--r--package/source/zipapi/sha1context.cxx119
-rw-r--r--package/source/zipapi/sha1context.hxx71
-rw-r--r--package/source/zippackage/ZipPackage.cxx1961
-rw-r--r--package/source/zippackage/ZipPackageBuffer.cxx128
-rw-r--r--package/source/zippackage/ZipPackageEntry.cxx125
-rw-r--r--package/source/zippackage/ZipPackageFolder.cxx425
-rw-r--r--package/source/zippackage/ZipPackageFolderEnumeration.cxx70
-rw-r--r--package/source/zippackage/ZipPackageFolderEnumeration.hxx49
-rw-r--r--package/source/zippackage/ZipPackageSink.cxx37
-rw-r--r--package/source/zippackage/ZipPackageSink.hxx38
-rw-r--r--package/source/zippackage/ZipPackageStream.cxx1359
-rw-r--r--package/source/zippackage/wrapstreamforshare.cxx152
-rw-r--r--package/source/zippackage/wrapstreamforshare.hxx59
-rw-r--r--package/source/zippackage/zipfileaccess.cxx479
-rw-r--r--package/util/package2.component39
150 files changed, 39099 insertions, 0 deletions
diff --git a/package/CppunitTest_package2_test.mk b/package/CppunitTest_package2_test.mk
new file mode 100644
index 0000000000..546da10ded
--- /dev/null
+++ b/package/CppunitTest_package2_test.mk
@@ -0,0 +1,40 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,package2_test))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,package2_test, \
+ package/qa/cppunit/test_package \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,package2_test, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ tl \
+ unotest \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,package2_test))
+
+$(eval $(call gb_CppunitTest_use_components,package2_test,\
+ configmgr/source/configmgr \
+ package/util/package2 \
+ ucb/source/core/ucb1 \
+ ucb/source/ucp/file/ucpfile1 \
+))
+
+$(eval $(call gb_CppunitTest_use_ure,package2_test))
+
+$(eval $(call gb_CppunitTest_use_configuration,package2_test))
+
+# vim: set noet sw=4 ts=4:
diff --git a/package/IwyuFilter_package.yaml b/package/IwyuFilter_package.yaml
new file mode 100644
index 0000000000..cd456c41cd
--- /dev/null
+++ b/package/IwyuFilter_package.yaml
@@ -0,0 +1,24 @@
+---
+assumeFilename: package/source/zippackage/ZipPackage.cxx
+excludelist:
+ package/source/manifest/ManifestExport.cxx:
+ # Actually used
+ - com/sun/star/beans/PropertyValue.hpp
+ package/source/manifest/ManifestImport.cxx:
+ # Actually used
+ - com/sun/star/xml/sax/XAttributeList.hpp
+ package/source/xstor/owriteablestream.cxx:
+ # Actually used
+ - com/sun/star/uno/XComponentContext.hpp
+ package/source/zippackage/ZipPackage.cxx:
+ # Actually used
+ - com/sun/star/beans/PropertyValue.hpp
+ package/source/zippackage/ZipPackageFolder.cxx:
+ # Actually used
+ - com/sun/star/beans/PropertyValue.hpp
+ package/source/zippackage/ZipPackageStream.cxx:
+ # Actually used
+ - com/sun/star/beans/PropertyValue.hpp
+ package/source/xstor/xstorage.cxx:
+ # Actually used
+ - com/sun/star/beans/PropertyValue.hpp
diff --git a/package/Library_package2.mk b/package/Library_package2.mk
new file mode 100644
index 0000000000..2ddbc31caf
--- /dev/null
+++ b/package/Library_package2.mk
@@ -0,0 +1,85 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,package2))
+
+$(eval $(call gb_Library_set_componentfile,package2,package/util/package2,services))
+
+$(eval $(call gb_Library_set_include,package2,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/package/inc \
+))
+
+$(eval $(call gb_Library_use_sdk_api,package2))
+
+$(eval $(call gb_Library_add_defs,package2,\
+ -DDLLIMPLEMENTATION_PACKAGE \
+))
+
+$(eval $(call gb_Library_set_precompiled_header,package2,package/inc/pch/precompiled_package2))
+
+$(eval $(call gb_Library_use_custom_headers,package2,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_Library_use_libraries,package2,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ sax \
+ salhelper \
+ ucbhelper \
+ tl \
+ utl \
+))
+
+$(eval $(call gb_Library_use_externals,package2,\
+ boost_headers \
+ argon2 \
+ zlib \
+))
+
+$(eval $(call gb_Library_add_exception_objects,package2,\
+ package/source/manifest/ManifestExport \
+ package/source/manifest/ManifestImport \
+ package/source/manifest/ManifestReader \
+ package/source/manifest/ManifestWriter \
+ package/source/zipapi/blowfishcontext \
+ package/source/zipapi/ByteChucker \
+ package/source/zipapi/ByteGrabber \
+ package/source/zipapi/CRC32 \
+ package/source/zipapi/Deflater \
+ package/source/zipapi/Inflater \
+ package/source/zipapi/sha1context \
+ package/source/zipapi/ThreadedDeflater \
+ package/source/zipapi/XBufferedThreadedStream \
+ package/source/zipapi/XUnbufferedStream \
+ package/source/zipapi/ZipEnumeration \
+ package/source/zipapi/ZipFile \
+ package/source/zipapi/ZipOutputEntry \
+ package/source/zipapi/ZipOutputStream \
+ package/source/zippackage/wrapstreamforshare \
+ package/source/zippackage/zipfileaccess \
+ package/source/zippackage/ZipPackageBuffer \
+ package/source/zippackage/ZipPackage \
+ package/source/zippackage/ZipPackageEntry \
+ package/source/zippackage/ZipPackageFolder \
+ package/source/zippackage/ZipPackageFolderEnumeration \
+ package/source/zippackage/ZipPackageSink \
+ package/source/zippackage/ZipPackageStream \
+))
+
+ifneq ($(SYSTEM_ZLIB),)
+$(eval $(call gb_Library_add_defs,package2,\
+ -DSYSTEM_ZLIB \
+))
+endif
+
+# vim: set noet sw=4 ts=4:
diff --git a/package/Library_xstor.mk b/package/Library_xstor.mk
new file mode 100644
index 0000000000..ea6b503e13
--- /dev/null
+++ b/package/Library_xstor.mk
@@ -0,0 +1,45 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,xstor))
+
+$(eval $(call gb_Library_set_componentfile,xstor,package/source/xstor/xstor,services))
+
+$(eval $(call gb_Library_set_include,xstor,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/package/inc \
+))
+
+$(eval $(call gb_Library_set_precompiled_header,xstor,package/inc/pch/precompiled_xstor))
+
+$(eval $(call gb_Library_use_sdk_api,xstor))
+
+$(eval $(call gb_Library_use_libraries,xstor,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ salhelper \
+ tl \
+ utl \
+))
+
+$(eval $(call gb_Library_add_exception_objects,xstor,\
+ package/source/xstor/disposelistener \
+ package/source/xstor/ocompinstream \
+ package/source/xstor/ohierarchyholder \
+ package/source/xstor/oseekinstream \
+ package/source/xstor/owriteablestream \
+ package/source/xstor/selfterminatefilestream \
+ package/source/xstor/switchpersistencestream \
+ package/source/xstor/xfactory \
+ package/source/xstor/xstorage \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/package/Makefile b/package/Makefile
new file mode 100644
index 0000000000..ccb1c85a04
--- /dev/null
+++ b/package/Makefile
@@ -0,0 +1,7 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+
+module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/package/Module_package.mk b/package/Module_package.mk
new file mode 100644
index 0000000000..ba9c09b5a4
--- /dev/null
+++ b/package/Module_package.mk
@@ -0,0 +1,22 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Module_Module,package))
+
+$(eval $(call gb_Module_add_targets,package,\
+ Library_package2 \
+ Library_xstor \
+ Package_dtd \
+))
+
+$(eval $(call gb_Module_add_check_targets,package,\
+ CppunitTest_package2_test \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/package/Package_dtd.mk b/package/Package_dtd.mk
new file mode 100644
index 0000000000..bfed3d60b5
--- /dev/null
+++ b/package/Package_dtd.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,package_dtd,$(SRCDIR)/package/dtd))
+
+$(eval $(call gb_Package_add_file,package_dtd,$(LIBO_SHARE_FOLDER)/dtd/officedocument/1_0/Manifest.dtd,Manifest.dtd))
+
+# vim: set noet sw=4 ts=4:
diff --git a/package/README.md b/package/README.md
new file mode 100644
index 0000000000..c85ce97904
--- /dev/null
+++ b/package/README.md
@@ -0,0 +1,3 @@
+# ZIP Support
+
+Reading and writing ZIP files.
diff --git a/package/dtd/Manifest.dtd b/package/dtd/Manifest.dtd
new file mode 100644
index 0000000000..9e16a007a7
--- /dev/null
+++ b/package/dtd/Manifest.dtd
@@ -0,0 +1,48 @@
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+<!ELEMENT manifest:manifest (manifest:file-entry+)>
+<!ATTLIST manifest:manifest xmlns:manifest CDATA #FIXED "http://openoffice.org/2001/manifest">
+
+<!ELEMENT manifest:file-entry (manifest:encryption-data?)>
+<!-- manifest:size is usually only specified for encrypted entries -->
+<!ATTLIST manifest:file-entry
+ manifest:full-path CDATA #REQUIRED
+ manifest:size CDATA #IMPLIED
+ manifest:media-type CDATA #REQUIRED
+>
+
+<!ELEMENT manifest:encryption-data (manifest:algorithm,manifest:key-derivation)>
+<!ATTLIST manifest:encryption-data
+ manifest:checksum-type CDATA #REQUIRED
+ manifest:checksum CDATA #REQUIRED >
+<!-- algorithm-name specifies the name of the algorithm used to encrypt
+ the stream, for example Blowfish
+ manifest:initialisation-vector is stored encoded in Base64 -->
+<!ELEMENT manifest:algorithm EMPTY>
+<!ATTLIST manifest:algorithm
+ manifest:algorithm-name CDATA #REQUIRED
+ manifest:initialisation-vector CDATA #REQUIRED>
+
+<!ELEMENT manifest:key-derivation EMPTY>
+<!-- manifest:key-derivation-name specifies the name of the algorithm used to derive
+ the key, for example PBKDF2 (see rfc 2898 )
+ manifest:salt is stored encoded in Base64 -->
+<!ATTLIST manifest:key-derivation
+ manifest:key-derivation-name CDATA #REQUIRED
+ manifest:salt CDATA #REQUIRED
+ manifest:iteration-count CDATA #REQUIRED>
diff --git a/package/inc/ByteChucker.hxx b/package/inc/ByteChucker.hxx
new file mode 100644
index 0000000000..f5d0202050
--- /dev/null
+++ b/package/inc/ByteChucker.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_BYTECHUCKER_HXX
+#define INCLUDED_PACKAGE_INC_BYTECHUCKER_HXX
+
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Reference.h>
+
+namespace com::sun::star {
+ namespace io { class XSeekable; class XOutputStream; }
+}
+class ByteChucker final
+{
+ css::uno::Reference < css::io::XOutputStream > xStream;
+ css::uno::Reference < css::io::XSeekable > xSeek;
+ css::uno::Sequence < sal_Int8 > a2Sequence, a4Sequence, a8Sequence;
+ sal_Int8 * const p2Sequence, * const p4Sequence, * const p8Sequence;
+
+public:
+ ByteChucker (css::uno::Reference<css::io::XOutputStream> const & xOstream);
+ ~ByteChucker();
+
+ /// @throws css::io::NotConnectedException
+ /// @throws css::io::BufferSizeExceededException
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void WriteBytes( const css::uno::Sequence< sal_Int8 >& aData );
+
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ sal_Int64 GetPosition();
+
+ void WriteInt16(sal_Int16 nInt16)
+ {
+ p2Sequence[0] = static_cast< sal_Int8 >((nInt16 >> 0 ) & 0xFF);
+ p2Sequence[1] = static_cast< sal_Int8 >((nInt16 >> 8 ) & 0xFF);
+ WriteBytes( a2Sequence );
+ }
+
+ void WriteInt32(sal_Int32 nInt32)
+ {
+ p4Sequence[0] = static_cast< sal_Int8 >((nInt32 >> 0 ) & 0xFF);
+ p4Sequence[1] = static_cast< sal_Int8 >((nInt32 >> 8 ) & 0xFF);
+ p4Sequence[2] = static_cast< sal_Int8 >((nInt32 >> 16 ) & 0xFF);
+ p4Sequence[3] = static_cast< sal_Int8 >((nInt32 >> 24 ) & 0xFF);
+ WriteBytes( a4Sequence );
+ }
+
+ void WriteUInt32(sal_uInt32 nuInt32)
+ {
+ p4Sequence[0] = static_cast < sal_Int8 > ((nuInt32 >> 0 ) & 0xFF);
+ p4Sequence[1] = static_cast < sal_Int8 > ((nuInt32 >> 8 ) & 0xFF);
+ p4Sequence[2] = static_cast < sal_Int8 > ((nuInt32 >> 16 ) & 0xFF);
+ p4Sequence[3] = static_cast < sal_Int8 > ((nuInt32 >> 24 ) & 0xFF);
+ WriteBytes( a4Sequence );
+ }
+
+ void WriteUInt64(sal_uInt64 nuInt64)
+ {
+ p8Sequence[0] = static_cast<sal_Int8>((nuInt64 >> 0) & 0xFF);
+ p8Sequence[1] = static_cast<sal_Int8>((nuInt64 >> 8) & 0xFF);
+ p8Sequence[2] = static_cast<sal_Int8>((nuInt64 >> 16) & 0xFF);
+ p8Sequence[3] = static_cast<sal_Int8>((nuInt64 >> 24) & 0xFF);
+ p8Sequence[4] = static_cast<sal_Int8>((nuInt64 >> 32) & 0xFF);
+ p8Sequence[5] = static_cast<sal_Int8>((nuInt64 >> 40) & 0xFF);
+ p8Sequence[6] = static_cast<sal_Int8>((nuInt64 >> 48) & 0xFF);
+ p8Sequence[7] = static_cast<sal_Int8>((nuInt64 >> 56) & 0xFF);
+ WriteBytes( a8Sequence );
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ByteGrabber.hxx b/package/inc/ByteGrabber.hxx
new file mode 100644
index 0000000000..ba1512cf51
--- /dev/null
+++ b/package/inc/ByteGrabber.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_BYTEGRABBER_HXX
+#define INCLUDED_PACKAGE_INC_BYTEGRABBER_HXX
+
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Reference.h>
+
+#include <mutex>
+
+namespace com::sun::star {
+ namespace io { class XSeekable; class XInputStream; }
+}
+class ByteGrabber final
+{
+ std::mutex m_aMutex;
+
+ css::uno::Reference < css::io::XInputStream > xStream;
+ css::uno::Reference < css::io::XSeekable > xSeek;
+ css::uno::Sequence < sal_Int8 > aSequence;
+ const sal_Int8 *pSequence;
+
+public:
+ ByteGrabber (css::uno::Reference < css::io::XInputStream > const & xIstream);
+ ~ByteGrabber();
+
+ void setInputStream (const css::uno::Reference < css::io::XInputStream >& xNewStream);
+ // XInputStream
+ /// @throws css::io::NotConnectedException
+ /// @throws css::io::BufferSizeExceededException
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ sal_Int32 readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead );
+ // XSeekable
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void seek( sal_Int64 location );
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ sal_Int64 getPosition( );
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ sal_Int64 getLength( );
+
+ sal_uInt16 ReadUInt16();
+ sal_uInt32 ReadUInt32();
+ sal_Int16 ReadInt16()
+ {
+ return static_cast<sal_Int16>(ReadUInt16());
+ }
+ sal_Int32 ReadInt32()
+ {
+ return static_cast<sal_Int32>(ReadUInt32());
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/CRC32.hxx b/package/inc/CRC32.hxx
new file mode 100644
index 0000000000..a2ca1b435e
--- /dev/null
+++ b/package/inc/CRC32.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_CRC32_HXX
+#define INCLUDED_PACKAGE_INC_CRC32_HXX
+
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Reference.h>
+
+namespace com::sun::star {
+ namespace io { class XInputStream; }
+}
+class CRC32 final
+{
+ sal_uInt32 nCRC;
+public:
+ CRC32();
+
+ /// @throws css::uno::RuntimeException
+ sal_Int64 updateStream (css::uno::Reference < css::io::XInputStream > const & xStream);
+ /// @throws css::uno::RuntimeException
+ void updateSegment(const css::uno::Sequence< sal_Int8 > &b, sal_Int32 len);
+ /// @throws css::uno::RuntimeException
+ void update(const css::uno::Sequence< sal_Int8 > &b);
+ /// @throws css::uno::RuntimeException
+ sal_Int32 getValue() const;
+ /// @throws css::uno::RuntimeException
+ void reset();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/EncryptedDataHeader.hxx b/package/inc/EncryptedDataHeader.hxx
new file mode 100644
index 0000000000..2f92dea60a
--- /dev/null
+++ b/package/inc/EncryptedDataHeader.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ENCRYPTEDDATAHEADER_HXX
+#define INCLUDED_PACKAGE_INC_ENCRYPTEDDATAHEADER_HXX
+
+#include <sal/types.h>
+
+/* The structure of this header is as follows:
+
+ Header signature 4 bytes
+ Version number 2 bytes
+ PBKDF2 Iteration count 4 bytes
+ Argon2 t_cost 4 bytes
+ Argon2 m_cost 4 bytes
+ Argon2 lanes 4 bytes
+ Size 4 bytes
+ EncAlgorithm 4 bytes
+ DigestAlgorithm 4 bytes
+ DerivedKeySize 4 bytes
+ StartKeyAlgorithm 4 bytes
+ Salt length 2 bytes
+ IV length 2 bytes
+ Digest length 2 bytes
+ MediaType length 2 bytes
+ Salt content X bytes
+ IV content X bytes
+ digest content X bytes
+ MediaType X bytes
+
+*/
+const sal_uInt32 n_ConstHeader = 0x05024d4dL; // "MM\002\005"
+const sal_Int32 n_ConstHeaderSize
+ = 50; // + salt length + iv length + digest length + mediatype length
+const sal_Int16 n_ConstCurrentVersion = 2;
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/EncryptionData.hxx b/package/inc/EncryptionData.hxx
new file mode 100644
index 0000000000..b505b9c227
--- /dev/null
+++ b/package/inc/EncryptionData.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ENCRYPTIONDATA_HXX
+#define INCLUDED_PACKAGE_INC_ENCRYPTIONDATA_HXX
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <cppuhelper/weak.hxx>
+
+#include <optional>
+#include <tuple>
+
+class BaseEncryptionData : public cppu::OWeakObject
+{
+public:
+ css::uno::Sequence< sal_Int8 > m_aSalt;
+ css::uno::Sequence< sal_Int8 > m_aInitVector;
+ css::uno::Sequence< sal_Int8 > m_aDigest;
+ ::std::optional<sal_Int32> m_oPBKDFIterationCount;
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> m_oArgon2Args;
+
+ BaseEncryptionData()
+ {
+ }
+
+ BaseEncryptionData( const BaseEncryptionData& aData )
+ : cppu::OWeakObject()
+ , m_aSalt( aData.m_aSalt )
+ , m_aInitVector( aData.m_aInitVector )
+ , m_aDigest( aData.m_aDigest )
+ , m_oPBKDFIterationCount(aData.m_oPBKDFIterationCount)
+ , m_oArgon2Args(aData.m_oArgon2Args)
+ {}
+};
+
+class EncryptionData final : public BaseEncryptionData
+{
+public:
+ css::uno::Sequence < sal_Int8 > m_aKey;
+ sal_Int32 m_nEncAlg;
+ ::std::optional<sal_Int32> m_oCheckAlg;
+ sal_Int32 m_nDerivedKeySize;
+ sal_Int32 m_nStartKeyGenID;
+ bool m_bTryWrongSHA1;
+
+ EncryptionData(const BaseEncryptionData& aData,
+ const css::uno::Sequence<sal_Int8>& aKey, sal_Int32 const nEncAlg,
+ ::std::optional<sal_Int32> const oCheckAlg,
+ sal_Int32 const nDerivedKeySize, sal_Int32 const nStartKeyGenID,
+ bool const bTryWrongSHA1)
+ : BaseEncryptionData( aData )
+ , m_aKey( aKey )
+ , m_nEncAlg( nEncAlg )
+ , m_oCheckAlg( oCheckAlg )
+ , m_nDerivedKeySize( nDerivedKeySize )
+ , m_nStartKeyGenID( nStartKeyGenID )
+ , m_bTryWrongSHA1(bTryWrongSHA1)
+ {}
+
+ EncryptionData( const EncryptionData& aData )
+ : BaseEncryptionData( aData )
+ , m_aKey( aData.m_aKey )
+ , m_nEncAlg( aData.m_nEncAlg )
+ , m_oCheckAlg( aData.m_oCheckAlg )
+ , m_nDerivedKeySize( aData.m_nDerivedKeySize )
+ , m_nStartKeyGenID( aData.m_nStartKeyGenID )
+ , m_bTryWrongSHA1(aData.m_bTryWrongSHA1)
+ {}
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/HashMaps.hxx b/package/inc/HashMaps.hxx
new file mode 100644
index 0000000000..000afa9e3b
--- /dev/null
+++ b/package/inc/HashMaps.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_HASHMAPS_HXX
+#define INCLUDED_PACKAGE_INC_HASHMAPS_HXX
+
+#include "ZipEntry.hxx"
+#include <unordered_map>
+
+class ZipPackageFolder;
+struct ZipContentInfo;
+
+typedef std::unordered_map < OUString,
+ ZipPackageFolder * > FolderHash;
+
+typedef std::unordered_map < OUString,
+ ZipEntry > EntryHash;
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/PackageConstants.hxx b/package/inc/PackageConstants.hxx
new file mode 100644
index 0000000000..15cc4d3ee9
--- /dev/null
+++ b/package/inc/PackageConstants.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_PACKAGECONSTANTS_HXX
+#define INCLUDED_PACKAGE_INC_PACKAGECONSTANTS_HXX
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+const sal_Int32 n_ConstBufferSize = 32768;
+
+// by calculation of the digest we read 32 bytes more ( if available )
+// it allows to ignore the padding if the stream is longer than n_ConstDigestDecrypt since we read at least two blocks more;
+// if the stream is shorter or equal the padding will be done successfully
+const sal_Int32 n_ConstDigestLength = 1024;
+const sal_Int32 n_ConstDigestDecrypt = 1056; // 1024 + 32
+
+// the constants related to the manifest.xml entries
+// these primarily exist so that ManifestImport can directly write into Sequence
+#define PKG_MNFST_FULLPATH 0 //FullPath (Put full-path property first for MBA)
+#define PKG_MNFST_VERSION 1 //Version
+#define PKG_MNFST_MEDIATYPE 2 //MediaType
+
+#define PKG_MNFST_INIVECTOR 3 //InitialisationVector
+#define PKG_MNFST_SALT 4 //Salt
+#define PKG_MNFST_ITERATION 5 //IterationCount
+#define PKG_MNFST_UCOMPSIZE 6 //Size
+#define PKG_MNFST_DIGEST 7 //Digest
+#define PKG_MNFST_ENCALG 8 //EncryptionAlgorithm
+#define PKG_MNFST_STARTALG 9 //StartKeyAlgorithm
+#define PKG_MNFST_DIGESTALG 10 //DigestAlgorithm
+#define PKG_MNFST_DERKEYSIZE 11 //DerivedKeySize
+#define PKG_MNFST_KDF 12 // KeyDerivationFunction
+#define PKG_MNFST_ARGON2ARGS 13 // Argon2 arguments
+#define PKG_MNFST_KEYINFO 14 // PGP KeyInfo
+
+#define PKG_SIZE_NOENCR_MNFST 3
+#define PKG_SIZE_ENCR_MNFST 15 // max size
+
+// the properties related constants
+inline constexpr OUString ENCRYPTION_KEY_PROPERTY = u"EncryptionKey"_ustr;
+inline constexpr OUString STORAGE_ENCRYPTION_KEYS_PROPERTY = u"StorageEncryptionKeys"_ustr;
+inline constexpr OUString ENCRYPTION_ALGORITHMS_PROPERTY = u"EncryptionAlgorithms"_ustr;
+inline constexpr OUString ENCRYPTION_GPG_PROPERTIES = u"EncryptionGpGProperties"_ustr;
+#define HAS_ENCRYPTED_ENTRIES_PROPERTY "HasEncryptedEntries"
+#define HAS_NONENCRYPTED_ENTRIES_PROPERTY "HasNonEncryptedEntries"
+#define IS_INCONSISTENT_PROPERTY "IsInconsistent"
+inline constexpr OUString MEDIATYPE_FALLBACK_USED_PROPERTY = u"MediaTypeFallbackUsed"_ustr;
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ThreadedDeflater.hxx b/package/inc/ThreadedDeflater.hxx
new file mode 100644
index 0000000000..361129d0f9
--- /dev/null
+++ b/package/inc/ThreadedDeflater.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_THREADEDDEFLATER_HXX
+#define INCLUDED_PACKAGE_THREADEDDEFLATER_HXX
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <comphelper/threadpool.hxx>
+#include <memory>
+#include <vector>
+#include <functional>
+
+namespace ZipUtils
+{
+/// Parallel compression a stream using the libz deflate algorithm.
+///
+/// Call deflateWrite() with the input stream and input/output processing functions.
+/// This will use multiple threads for compression on each batch of data from the stream.
+class ThreadedDeflater final
+{
+ class Task;
+ // Note: All this should be lock-less. Each task writes only to its part
+ // of the data.
+ std::vector<std::vector<sal_Int8>> outBuffers;
+ std::shared_ptr<comphelper::ThreadTaskTag> threadTaskTag;
+ css::uno::Sequence<sal_Int8> inBuffer;
+ css::uno::Sequence<sal_Int8> prevDataBlock;
+ std::function<void(const css::uno::Sequence<sal_Int8>&, sal_Int32)> maProcessOutputFunc;
+ sal_Int64 totalIn;
+ sal_Int64 totalOut;
+ int zlibLevel;
+
+public:
+ // Unlike with Deflater class, bNoWrap is always true.
+ ThreadedDeflater(sal_Int32 nSetLevel);
+ ~ThreadedDeflater() COVERITY_NOEXCEPT_FALSE;
+ void deflateWrite(
+ const css::uno::Reference<css::io::XInputStream>& xInStream,
+ std::function<void(const css::uno::Sequence<sal_Int8>&, sal_Int32)> aProcessInputFunc,
+ std::function<void(const css::uno::Sequence<sal_Int8>&, sal_Int32)> aProcessOutputFunc);
+ sal_Int64 getTotalIn() const { return totalIn; }
+ sal_Int64 getTotalOut() const { return totalOut; }
+
+private:
+ void processDeflatedBuffers();
+ void clear();
+};
+
+} // namespace
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipEntry.hxx b/package/inc/ZipEntry.hxx
new file mode 100644
index 0000000000..db87f660d5
--- /dev/null
+++ b/package/inc/ZipEntry.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ZIPENTRY_HXX
+#define INCLUDED_PACKAGE_INC_ZIPENTRY_HXX
+
+#include <rtl/ustring.hxx>
+
+struct ZipEntry
+{
+ sal_Int16 nVersion;
+ sal_Int16 nFlag;
+ sal_Int16 nMethod;
+ sal_Int32 nTime;
+ sal_Int32 nCrc;
+ sal_Int64 nCompressedSize;
+ sal_Int64 nSize;
+ sal_Int64 nOffset;
+ sal_Int16 nPathLen;
+ sal_Int16 nExtraLen;
+ OUString sPath;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipEnumeration.hxx b/package/inc/ZipEnumeration.hxx
new file mode 100644
index 0000000000..50533cc289
--- /dev/null
+++ b/package/inc/ZipEnumeration.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "HashMaps.hxx"
+
+class ZipEnumeration final
+{
+ EntryHash& rEntryHash;
+ EntryHash::const_iterator aIterator;
+
+public:
+ bool hasMoreElements();
+ const ZipEntry* nextElement();
+ ZipEnumeration(EntryHash& rNewEntryHash);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipFile.hxx b/package/inc/ZipFile.hxx
new file mode 100644
index 0000000000..2d42ed4031
--- /dev/null
+++ b/package/inc/ZipFile.hxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ZIPFILE_HXX
+#define INCLUDED_PACKAGE_INC_ZIPFILE_HXX
+
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+
+#include <comphelper/refcountedmutex.hxx>
+#include <package/Inflater.hxx>
+#include <rtl/ref.hxx>
+#include "ByteGrabber.hxx"
+#include "HashMaps.hxx"
+#include "EncryptionData.hxx"
+
+class MemoryByteGrabber;
+namespace com::sun::star {
+ namespace uno { class XComponentContext; }
+ namespace ucb { class XProgressHandler; }
+}
+namespace rtl
+{
+ template < class T > class Reference;
+}
+
+/*
+ * We impose arbitrary but reasonable limit on ZIP files.
+ */
+
+#define ZIP_MAXNAMELEN 512
+#define ZIP_MAXENTRIES (0x10000 - 2)
+
+class ZipEnumeration;
+
+class ZipFile
+{
+ rtl::Reference<comphelper::RefCountedMutex> m_aMutexHolder;
+
+ EntryHash aEntries;
+ ByteGrabber aGrabber;
+ ZipUtils::Inflater aInflater;
+ css::uno::Reference < css::io::XInputStream > xStream;
+ const css::uno::Reference < css::uno::XComponentContext > m_xContext;
+
+ bool bRecoveryMode;
+
+ // aMediaType parameter is used only for raw stream header creation
+ css::uno::Reference < css::io::XInputStream > createStreamForZipEntry(
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
+ ZipEntry const & rEntry,
+ const ::rtl::Reference < EncryptionData > &rData,
+ sal_Int8 nStreamMode,
+ bool bDecrypt,
+ const bool bUseBufferedStream = true,
+ const OUString& aMediaType = OUString() );
+
+ css::uno::Reference<css::io::XInputStream> checkValidPassword(
+ ZipEntry const& rEntry, rtl::Reference<EncryptionData> const& rData,
+ rtl::Reference<comphelper::RefCountedMutex> const& rMutexHolder);
+
+ bool checkSizeAndCRC( const ZipEntry& aEntry );
+
+ sal_Int32 getCRC( sal_Int64 nOffset, sal_Int64 nSize );
+
+ void getSizeAndCRC( sal_Int64 nOffset, sal_Int64 nCompressedSize, sal_Int64 *nSize, sal_Int32 *nCRC );
+
+ void readLOC( ZipEntry &rEntry );
+ sal_Int32 readCEN();
+ sal_Int32 findEND();
+ void recover();
+ static void readExtraFields(MemoryByteGrabber& aMemGrabber, sal_Int16 nExtraLen,
+ sal_uInt64& nSize, sal_uInt64& nCompressedSize,
+ sal_uInt64* nOffset);
+
+public:
+
+ ZipFile( rtl::Reference<comphelper::RefCountedMutex> aMutexHolder,
+ css::uno::Reference < css::io::XInputStream > const &xInput,
+ css::uno::Reference < css::uno::XComponentContext > xContext,
+ bool bInitialise );
+
+ ZipFile( rtl::Reference<comphelper::RefCountedMutex> aMutexHolder,
+ css::uno::Reference < css::io::XInputStream > const &xInput,
+ css::uno::Reference < css::uno::XComponentContext > xContext,
+ bool bInitialise,
+ bool bForceRecover );
+
+ ~ZipFile();
+
+ EntryHash& GetEntryHash() { return aEntries; }
+
+ void setInputStream ( const css::uno::Reference < css::io::XInputStream >& xNewStream );
+ css::uno::Reference< css::io::XInputStream > getRawData(
+ ZipEntry& rEntry,
+ const ::rtl::Reference < EncryptionData > &rData,
+ bool bDecrypt,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
+ const bool bUseBufferedStream = true );
+
+ static css::uno::Reference< css::xml::crypto::XDigestContext > StaticGetDigestContextForChecksum(
+ const css::uno::Reference< css::uno::XComponentContext >& xArgContext,
+ const ::rtl::Reference< EncryptionData >& xEncryptionData );
+
+ static css::uno::Reference< css::xml::crypto::XCipherContext > StaticGetCipher(
+ const css::uno::Reference< css::uno::XComponentContext >& xArgContext,
+ const ::rtl::Reference< EncryptionData >& xEncryptionData,
+ bool bEncrypt );
+
+ static void StaticFillHeader ( const ::rtl::Reference < EncryptionData > & rData,
+ sal_Int64 nSize,
+ const OUString& aMediaType,
+ sal_Int8 * & pHeader );
+
+ static bool StaticFillData ( ::rtl::Reference < BaseEncryptionData > const & rData,
+ sal_Int32 &rEncAlgorithm,
+ sal_Int32 &rChecksumAlgorithm,
+ sal_Int32 &rDerivedKeySize,
+ sal_Int32 &rStartKeyGenID,
+ sal_Int32 &rSize,
+ OUString& aMediaType,
+ const css::uno::Reference < css::io::XInputStream >& rStream );
+
+ static css::uno::Reference< css::io::XInputStream > StaticGetDataFromRawStream(
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::io::XInputStream >& xStream,
+ const ::rtl::Reference < EncryptionData > &rData );
+
+ static bool StaticHasValidPassword (
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< sal_Int8 > &aReadBuffer,
+ const ::rtl::Reference < EncryptionData > &rData );
+
+ css::uno::Reference< css::io::XInputStream > getInputStream(
+ ZipEntry& rEntry,
+ const ::rtl::Reference < EncryptionData > &rData,
+ bool bDecrypt,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder );
+
+ css::uno::Reference< css::io::XInputStream > getDataStream(
+ ZipEntry& rEntry,
+ const ::rtl::Reference < EncryptionData > &rData,
+ bool bDecrypt,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder );
+
+ css::uno::Reference< css::io::XInputStream > getWrappedRawStream(
+ ZipEntry& rEntry,
+ const ::rtl::Reference < EncryptionData > &rData,
+ const OUString& aMediaType,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder );
+
+ ZipEnumeration entries();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipOutputEntry.hxx b/package/inc/ZipOutputEntry.hxx
new file mode 100644
index 0000000000..94eb988d8b
--- /dev/null
+++ b/package/inc/ZipOutputEntry.hxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ZIPOUTPUTENTRY_HXX
+#define INCLUDED_PACKAGE_INC_ZIPOUTPUTENTRY_HXX
+
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XTempFile.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+
+#include <package/Deflater.hxx>
+#include <comphelper/threadpool.hxx>
+#include <unotools/tempfile.hxx>
+#include "CRC32.hxx"
+#include <atomic>
+#include <exception>
+
+struct ZipEntry;
+class ZipPackageBuffer;
+class ZipPackageStream;
+
+class ZipOutputEntryBase
+{
+protected:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::io::XOutputStream > m_xOutStream;
+
+ css::uno::Reference< css::xml::crypto::XCipherContext > m_xCipherContext;
+ css::uno::Reference< css::xml::crypto::XDigestContext > m_xDigestContext;
+
+ CRC32 m_aCRC;
+ ZipEntry *m_pCurrentEntry;
+ sal_Int16 m_nDigested;
+ ZipPackageStream* m_pCurrentStream;
+ bool m_bEncryptCurrentEntry;
+
+public:
+ virtual ~ZipOutputEntryBase() = default;
+
+ virtual void writeStream(const css::uno::Reference< css::io::XInputStream >& xInStream) = 0;
+
+ ZipEntry* getZipEntry() { return m_pCurrentEntry; }
+ ZipPackageStream* getZipPackageStream() { return m_pCurrentStream; }
+ bool isEncrypt() const { return m_bEncryptCurrentEntry; }
+
+ void closeEntry();
+
+protected:
+ ZipOutputEntryBase(
+ css::uno::Reference< css::io::XOutputStream > xOutStream,
+ css::uno::Reference< css::uno::XComponentContext > xContext,
+ ZipEntry& rEntry, ZipPackageStream* pStream, bool bEncrypt, bool checkStream);
+
+ // Inherited classes call this with deflated data buffer.
+ void processDeflated( const css::uno::Sequence< sal_Int8 >& deflateBuffer, sal_Int32 nLength );
+ // Inherited classes call this with the input buffer.
+ void processInput( const css::uno::Sequence< sal_Int8 >& rBuffer );
+
+ virtual void finishDeflater() = 0;
+ virtual sal_Int64 getDeflaterTotalIn() const = 0;
+ virtual sal_Int64 getDeflaterTotalOut() const = 0;
+ virtual void deflaterReset() = 0;
+ virtual bool isDeflaterFinished() const = 0;
+};
+
+// Normal non-threaded case.
+class ZipOutputEntry : public ZipOutputEntryBase
+{
+ css::uno::Sequence< sal_Int8 > m_aDeflateBuffer;
+ ZipUtils::Deflater m_aDeflater;
+
+public:
+ ZipOutputEntry(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutStream,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry, ZipPackageStream* pStream, bool bEncrypt);
+ void writeStream(const css::uno::Reference< css::io::XInputStream >& xInStream) override;
+ void write(const css::uno::Sequence< sal_Int8 >& rBuffer);
+
+protected:
+ ZipOutputEntry(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutStream,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry, ZipPackageStream* pStream, bool bEncrypt, bool checkStream);
+ virtual void finishDeflater() override;
+ virtual sal_Int64 getDeflaterTotalIn() const override;
+ virtual sal_Int64 getDeflaterTotalOut() const override;
+ virtual void deflaterReset() override;
+ virtual bool isDeflaterFinished() const override;
+ void doDeflate();
+};
+
+// Class that runs the compression in a background thread.
+class ZipOutputEntryInThread final : public ZipOutputEntry
+{
+ class Task;
+ rtl::Reference<utl::TempFileFastService> m_xTempFile;
+ std::exception_ptr m_aParallelDeflateException;
+ std::atomic<bool> m_bFinished;
+
+public:
+ ZipOutputEntryInThread(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry, ZipPackageStream* pStream, bool bEncrypt);
+ std::unique_ptr<comphelper::ThreadTask> createTask(
+ const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
+ const css::uno::Reference< css::io::XInputStream >& xInStream );
+ /* This block of methods is for threaded zipping, where we compress to a temp stream, whose
+ data is retrieved via getData */
+ void createBufferFile();
+ void setParallelDeflateException(const std::exception_ptr& exception) { m_aParallelDeflateException = exception; }
+ css::uno::Reference< css::io::XInputStream > getData() const;
+ const std::exception_ptr& getParallelDeflateException() const { return m_aParallelDeflateException; }
+ void closeBufferFile();
+ void deleteBufferFile();
+ bool isFinished() const { return m_bFinished; }
+private:
+ void setFinished() { m_bFinished = true; }
+};
+
+// Class that synchronously runs the compression in multiple threads (using ThreadDeflater).
+class ZipOutputEntryParallel final : public ZipOutputEntryBase
+{
+ sal_Int64 totalIn;
+ sal_Int64 totalOut;
+ bool finished;
+public:
+ ZipOutputEntryParallel(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutStream,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry, ZipPackageStream* pStream, bool bEncrypt);
+ void writeStream(const css::uno::Reference< css::io::XInputStream >& xInStream) override;
+private:
+ virtual void finishDeflater() override;
+ virtual sal_Int64 getDeflaterTotalIn() const override;
+ virtual sal_Int64 getDeflaterTotalOut() const override;
+ virtual void deflaterReset() override;
+ virtual bool isDeflaterFinished() const override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipOutputStream.hxx b/package/inc/ZipOutputStream.hxx
new file mode 100644
index 0000000000..d92f140f70
--- /dev/null
+++ b/package/inc/ZipOutputStream.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ZIPOUTPUTSTREAM_HXX
+#define INCLUDED_PACKAGE_INC_ZIPOUTPUTSTREAM_HXX
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/io/XOutputStream.hpp>
+
+#include "ByteChucker.hxx"
+#include <comphelper/threadpool.hxx>
+
+#include <cstddef>
+#include <exception>
+#include <vector>
+
+struct ZipEntry;
+class ZipOutputEntry;
+class ZipOutputEntryInThread;
+class ZipPackageStream;
+
+class ZipOutputStream
+{
+ css::uno::Reference< css::io::XOutputStream > m_xStream;
+ ::std::vector < ZipEntry * > m_aZipList;
+ std::shared_ptr<comphelper::ThreadTaskTag> mpThreadTaskTag;
+
+ ByteChucker m_aChucker;
+ ZipEntry *m_pCurrentEntry;
+ std::vector< ZipOutputEntryInThread* > m_aEntries;
+ std::exception_ptr m_aDeflateException;
+
+public:
+ ZipOutputStream(
+ const css::uno::Reference< css::io::XOutputStream > &xOStream );
+ ~ZipOutputStream();
+
+ void addDeflatingThreadTask( ZipOutputEntryInThread *pEntry, std::unique_ptr<comphelper::ThreadTask> pThreadTask );
+
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void writeLOC( ZipEntry *pEntry, bool bEncrypt = false );
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void rawWrite( const css::uno::Sequence< sal_Int8 >& rBuffer );
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void rawCloseEntry( bool bEncrypt = false );
+
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void finish();
+ const css::uno::Reference< css::io::XOutputStream >& getStream() const;
+
+ static sal_uInt32 getCurrentDosTime();
+ static void setEntry( ZipEntry *pEntry );
+
+private:
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void writeEND(sal_uInt32 nOffset, sal_uInt32 nLength);
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void writeCEN( const ZipEntry &rEntry );
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void writeDataDescriptor( const ZipEntry &rEntry );
+ void writeExtraFields( const ZipEntry& rEntry );
+
+ // ScheduledThread handling helpers
+ void consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread> pCandidate);
+ void consumeFinishedScheduledThreadTaskEntries();
+
+public:
+ void reduceScheduledThreadTasksToGivenNumberOrLess(
+ std::size_t nThreadTasks);
+
+ const std::shared_ptr<comphelper::ThreadTaskTag>& getThreadTaskTag() const { return mpThreadTaskTag; }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipPackage.hxx b/package/inc/ZipPackage.hxx
new file mode 100644
index 0000000000..dbfbfe1bc1
--- /dev/null
+++ b/package/inc/ZipPackage.hxx
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ZIPPACKAGE_HXX
+#define INCLUDED_PACKAGE_INC_ZIPPACKAGE_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <comphelper/refcountedmutex.hxx>
+#include <rtl/ref.hxx>
+#include <o3tl/unreachable.hxx>
+
+#include "HashMaps.hxx"
+#include "ZipFile.hxx"
+#include <vector>
+#include <optional>
+
+class SvStream;
+class ZipOutputStream;
+class ZipPackageFolder;
+class ZipFile;
+namespace com::sun::star {
+ namespace container { class XNameContainer; }
+ namespace io { class XStream; class XOutputStream; class XInputStream; class XSeekable; class XActiveDataStreamer; }
+ namespace lang { class XMultiServiceFactory; }
+ namespace uno { class XComponentContext; }
+ namespace task { class XInteractionHandler; }
+}
+
+enum InitialisationMode
+{
+ e_IMode_None,
+ e_IMode_URL,
+ e_IMode_XInputStream,
+ e_IMode_XStream
+};
+
+class ZipPackage final : public cppu::WeakImplHelper
+ <
+ css::lang::XInitialization,
+ css::lang::XSingleServiceFactory,
+ css::lang::XServiceInfo,
+ css::container::XHierarchicalNameAccess,
+ css::util::XChangesBatch,
+ css::beans::XPropertySet
+ >
+{
+ rtl::Reference<comphelper::RefCountedMutex> m_aMutexHolder;
+
+ css::uno::Sequence< css::beans::NamedValue > m_aStorageEncryptionKeys;
+ css::uno::Sequence< sal_Int8 > m_aEncryptionKey;
+ css::uno::Sequence< css::uno::Sequence< css::beans::NamedValue > > m_aGpgProps;
+
+ FolderHash m_aRecent;
+ OUString m_aURL;
+
+ sal_Int32 m_nStartKeyGenerationID;
+ ::std::optional<sal_Int32> m_oChecksumDigestID;
+ sal_Int32 m_nKeyDerivationFunctionID;
+ sal_Int32 m_nCommonEncryptionID;
+ bool m_bHasEncryptedEntries;
+ bool m_bHasNonEncryptedEntries;
+
+ bool m_bInconsistent;
+ bool m_bForceRecovery;
+
+ bool m_bMediaTypeFallbackUsed;
+ sal_Int32 m_nFormat;
+ bool m_bAllowRemoveOnInsert;
+
+ InitialisationMode m_eMode;
+
+ rtl::Reference < ZipPackageFolder > m_xRootFolder;
+ css::uno::Reference < css::io::XStream > m_xStream;
+ css::uno::Reference < css::io::XInputStream > m_xContentStream;
+ css::uno::Reference < css::io::XSeekable > m_xContentSeek;
+ const css::uno::Reference < css::uno::XComponentContext > m_xContext;
+
+ std::optional<ZipFile> m_pZipFile;
+ bool m_bDisableFileSync = false;
+
+ bool isLocalFile() const;
+
+ void parseManifest();
+ void parseContentType();
+ void getZipFileContents();
+
+ void WriteMimetypeMagicFile( ZipOutputStream& aZipOut );
+ void WriteManifest( ZipOutputStream& aZipOut, const ::std::vector< css::uno::Sequence< css::beans::PropertyValue > >& aManList );
+ void WriteContentTypes( ZipOutputStream& aZipOut, const ::std::vector< css::uno::Sequence< css::beans::PropertyValue > >& aManList );
+
+ css::uno::Reference< css::io::XInputStream > writeTempFile();
+ css::uno::Reference < css::io::XActiveDataStreamer > openOriginalForOutput();
+ void DisconnectFromTargetAndThrowException_Impl(
+ const css::uno::Reference< css::io::XInputStream >& xTempStream );
+
+public:
+ ZipPackage( css::uno::Reference < css::uno::XComponentContext > xContext );
+ virtual ~ZipPackage() override;
+ ZipFile& getZipFile() { return *m_pZipFile;}
+ sal_Int32 getFormat() const { return m_nFormat; }
+
+ sal_Int32 GetStartKeyGenID() const { return m_nStartKeyGenerationID; }
+ sal_Int32 GetEncAlgID() const { return m_nCommonEncryptionID; }
+ ::std::optional<sal_Int32> GetChecksumAlgID() const { return m_oChecksumDigestID; }
+ sal_Int32 GetDefaultDerivedKeySize() const {
+ switch (m_nCommonEncryptionID)
+ {
+ case css::xml::crypto::CipherID::BLOWFISH_CFB_8:
+ return 16;
+ case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
+ case css::xml::crypto::CipherID::AES_GCM_W3C:
+ return 32;
+ default:
+ O3TL_UNREACHABLE;
+ }
+ }
+
+ rtl::Reference<comphelper::RefCountedMutex>& GetSharedMutexRef() { return m_aMutexHolder; }
+
+ void ConnectTo( const css::uno::Reference< css::io::XInputStream >& xInStream );
+ css::uno::Sequence< sal_Int8 > GetEncryptionKey();
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+ // XHierarchicalNameAccess
+ virtual css::uno::Any SAL_CALL getByHierarchicalName( const OUString& aName ) override;
+ virtual sal_Bool SAL_CALL hasByHierarchicalName( const OUString& aName ) override;
+ // XSingleServiceFactory
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( ) override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+ // XChangesBatch
+ virtual void SAL_CALL commitChanges( ) override;
+ virtual sal_Bool SAL_CALL hasPendingChanges( ) override;
+ virtual css::uno::Sequence< css::util::ElementChange > SAL_CALL getPendingChanges( ) override;
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+#endif
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportZip(SvStream& rStream);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipPackageBuffer.hxx b/package/inc/ZipPackageBuffer.hxx
new file mode 100644
index 0000000000..64a24563cb
--- /dev/null
+++ b/package/inc/ZipPackageBuffer.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ZIPPACKAGEBUFFER_HXX
+#define INCLUDED_PACKAGE_INC_ZIPPACKAGEBUFFER_HXX
+
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <cppuhelper/implbase.hxx>
+
+class ZipPackageBuffer final : public ::cppu::WeakImplHelper
+<
+ css::io::XInputStream,
+ css::io::XOutputStream,
+ css::io::XSeekable
+>
+{
+ css::uno::Sequence < sal_Int8 > m_aBuffer;
+ sal_Int64 m_nBufferSize, m_nEnd, m_nCurrent;
+ bool m_bMustInitBuffer;
+public:
+ ZipPackageBuffer();
+ virtual ~ZipPackageBuffer() override;
+
+ void realloc ( sal_Int32 nSize ) { m_aBuffer.realloc ( nSize ); }
+ const css::uno::Sequence < sal_Int8>& getSequence () const { return m_aBuffer; }
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+ // XOutputStream
+ virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override;
+ virtual void SAL_CALL flush( ) override;
+ virtual void SAL_CALL closeOutput( ) override;
+ // XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+ virtual sal_Int64 SAL_CALL getPosition( ) override;
+ virtual sal_Int64 SAL_CALL getLength( ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipPackageEntry.hxx b/package/inc/ZipPackageEntry.hxx
new file mode 100644
index 0000000000..f25cdc19bd
--- /dev/null
+++ b/package/inc/ZipPackageEntry.hxx
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ZIPPACKAGEENTRY_HXX
+#define INCLUDED_PACKAGE_INC_ZIPPACKAGEENTRY_HXX
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "ZipEntry.hxx"
+#include <cppuhelper/implbase.hxx>
+
+#include <vector>
+#include <optional>
+#include <tuple>
+
+typedef void* rtlRandomPool;
+class ZipOutputStream;
+class ZipPackageFolder;
+
+class ZipPackageEntry : public cppu::WeakImplHelper
+<
+ css::container::XNamed,
+ css::container::XChild,
+ css::beans::XPropertySet,
+ css::lang::XServiceInfo
+>
+{
+protected:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ OUString msName;
+ bool mbIsFolder:1;
+ bool mbAllowRemoveOnInsert:1;
+ OUString msMediaType;
+ ZipPackageFolder* mpParent;
+ sal_Int32 m_nFormat;
+
+public:
+ ZipEntry aEntry;
+ ZipPackageEntry();
+ virtual ~ZipPackageEntry() override;
+
+ const OUString& GetMediaType() const { return msMediaType; }
+ void SetMediaType(const OUString & sNewType) { msMediaType = sNewType; }
+ void doSetParent(ZipPackageFolder * pNewParent);
+ bool IsFolder() const { return mbIsFolder; }
+ void SetFolder(const bool bSetFolder) { mbIsFolder = bSetFolder; }
+
+ virtual bool saveChild( const OUString &rPath,
+ std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const css::uno::Sequence < sal_Int8 >& rEncryptionKey,
+ ::std::optional<sal_Int32> oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args,
+ const rtlRandomPool &rRandomPool ) = 0;
+
+ void clearParent()
+ {
+ // xParent.clear();
+ mpParent = nullptr;
+ }
+ // XNamed
+ virtual OUString SAL_CALL getName( ) override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+ // XChild
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override;
+ virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override;
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override = 0;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override = 0;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipPackageFolder.hxx b/package/inc/ZipPackageFolder.hxx
new file mode 100644
index 0000000000..2b1b981913
--- /dev/null
+++ b/package/inc/ZipPackageFolder.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ZIPPACKAGEFOLDER_HXX
+#define INCLUDED_PACKAGE_INC_ZIPPACKAGEFOLDER_HXX
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "ZipPackageEntry.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+
+class ZipOutputStream;
+struct ZipEntry;
+class ZipPackageFolder;
+class ZipPackageStream;
+
+struct ZipContentInfo
+{
+ rtl::Reference < ZipPackageEntry > xPackageEntry;
+ bool bFolder;
+ union
+ {
+ ZipPackageFolder *pFolder;
+ ZipPackageStream *pStream;
+ };
+ ZipContentInfo( ZipPackageStream * pNewStream );
+ ZipContentInfo( ZipPackageFolder * pNewFolder );
+ ZipContentInfo( const ZipContentInfo& );
+ ZipContentInfo( ZipContentInfo&& );
+ ZipContentInfo& operator=( const ZipContentInfo& );
+ ZipContentInfo& operator=( ZipContentInfo&& );
+
+ ~ZipContentInfo();
+};
+
+typedef std::unordered_map < OUString,
+ ZipContentInfo > ContentHash;
+
+class ZipPackageFolder final : public cppu::ImplInheritanceHelper
+<
+ ZipPackageEntry,
+ css::container::XNameContainer,
+ css::container::XEnumerationAccess
+>
+{
+private:
+ ContentHash maContents;
+ OUString m_sVersion;
+
+public:
+
+ ZipPackageFolder( const css::uno::Reference < css::uno::XComponentContext >& xContext,
+ sal_Int32 nFormat,
+ bool bAllowRemoveOnInsert );
+ virtual ~ZipPackageFolder() override;
+
+ const OUString& GetVersion() const { return m_sVersion; }
+ void SetVersion( const OUString& aVersion ) { m_sVersion = aVersion; }
+
+ bool LookForUnexpectedODF12Streams(std::u16string_view aPath, bool isWholesomeEncryption);
+
+ void setChildStreamsTypeByExtension( const css::beans::StringPair& aPair );
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::container::ElementExistException
+ /// @throws css::lang::WrappedTargetException
+ /// @throws css::uno::RuntimeException
+ void doInsertByName ( ZipPackageEntry *pEntry, bool bSetParent );
+
+ ZipContentInfo& doGetByName( const OUString& aName );
+
+ void setPackageFormat_Impl( sal_Int32 nFormat ) { m_nFormat = nFormat; }
+ void setRemoveOnInsertMode_Impl( bool bRemove ) { mbAllowRemoveOnInsert = bRemove; }
+
+ virtual bool saveChild( const OUString &rPath,
+ std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const css::uno::Sequence < sal_Int8 >& rEncryptionKey,
+ ::std::optional<sal_Int32> oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args,
+ const rtlRandomPool &rRandomPool ) override;
+
+ // Recursive functions
+ /// @throws css::uno::RuntimeException
+ void saveContents(
+ const OUString &rPath,
+ std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const css::uno::Sequence< sal_Int8 > &rEncryptionKey,
+ ::std::optional<sal_Int32> oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args,
+ const rtlRandomPool & rRandomPool) const;
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration( ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override;
+
+ // XPropertySet
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/ZipPackageStream.hxx b/package/inc/ZipPackageStream.hxx
new file mode 100644
index 0000000000..0cb52e88c8
--- /dev/null
+++ b/package/inc/ZipPackageStream.hxx
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_INC_ZIPPACKAGESTREAM_HXX
+#define INCLUDED_PACKAGE_INC_ZIPPACKAGESTREAM_HXX
+
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/packages/XDataSinkEncrSupport.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "ZipPackageEntry.hxx"
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include "EncryptionData.hxx"
+
+
+#define PACKAGE_STREAM_NOTSET 0
+#define PACKAGE_STREAM_PACKAGEMEMBER 1
+#define PACKAGE_STREAM_DETECT 2
+#define PACKAGE_STREAM_DATA 3
+#define PACKAGE_STREAM_RAW 4
+
+class ZipPackage;
+struct ZipEntry;
+class ZipPackageStream final : public cppu::ImplInheritanceHelper
+<
+ ZipPackageEntry,
+ css::io::XActiveDataSink,
+ css::packages::XDataSinkEncrSupport
+>
+{
+private:
+ css::uno::Reference < css::io::XInputStream > m_xStream;
+ ZipPackage &m_rZipPackage;
+ bool m_bToBeCompressed, m_bToBeEncrypted, m_bHaveOwnKey, m_bIsEncrypted;
+
+ ::rtl::Reference< BaseEncryptionData > m_xBaseEncryptionData;
+ css::uno::Sequence< css::beans::NamedValue > m_aStorageEncryptionKeys;
+ css::uno::Sequence< sal_Int8 > m_aEncryptionKey;
+
+ sal_Int32 m_nImportedStartKeyAlgorithm;
+ sal_Int32 m_nImportedEncryptionAlgorithm;
+ ::std::optional<sal_Int32> m_oImportedChecksumAlgorithm;
+ sal_Int32 m_nImportedDerivedKeySize;
+
+ sal_uInt8 m_nStreamMode;
+ sal_uInt32 m_nMagicalHackPos;
+ sal_uInt32 m_nMagicalHackSize;
+ sal_Int64 m_nOwnStreamOrigSize;
+
+ bool m_bHasSeekable;
+ bool m_bCompressedIsSetFromOutside;
+ bool m_bFromManifest;
+ bool m_bUseWinEncoding;
+ bool m_bRawStream;
+
+ /// Check that m_xStream implements io::XSeekable and return it
+ css::uno::Reference< css::io::XInputStream > const & GetOwnSeekStream();
+ /// get raw data using unbuffered stream
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XInputStream > getRawData();
+
+public:
+ bool IsPackageMember () const { return m_nStreamMode == PACKAGE_STREAM_PACKAGEMEMBER;}
+
+ bool IsFromManifest() const { return m_bFromManifest; }
+ void SetFromManifest( bool bValue ) { m_bFromManifest = bValue; }
+
+ enum class Bugs { None, WinEncodingWrongSHA1, WrongSHA1 };
+ ::rtl::Reference<EncryptionData> GetEncryptionData(Bugs bugs = Bugs::None);
+
+ css::uno::Sequence<sal_Int8> GetEncryptionKey(Bugs bugs = Bugs::None);
+
+ sal_Int32 GetStartKeyGenID() const;
+
+ sal_Int32 GetEncryptionAlgorithm() const;
+ sal_Int32 GetIVSize() const;
+
+ void SetToBeCompressed (bool bNewValue) { m_bToBeCompressed = bNewValue;}
+ void SetIsEncrypted (bool bNewValue) { m_bIsEncrypted = bNewValue;}
+ void SetImportedStartKeyAlgorithm( sal_Int32 nAlgorithm ) { m_nImportedStartKeyAlgorithm = nAlgorithm; }
+ void SetImportedEncryptionAlgorithm( sal_Int32 nAlgorithm ) { m_nImportedEncryptionAlgorithm = nAlgorithm; }
+ void SetImportedChecksumAlgorithm(::std::optional<sal_Int32> const& roAlgorithm) { m_oImportedChecksumAlgorithm = roAlgorithm; }
+ void SetImportedDerivedKeySize( sal_Int32 nSize ) { m_nImportedDerivedKeySize = nSize; }
+ void SetToBeEncrypted (bool bNewValue)
+ {
+ m_bToBeEncrypted = bNewValue;
+ if ( m_bToBeEncrypted && !m_xBaseEncryptionData.is())
+ m_xBaseEncryptionData = new BaseEncryptionData;
+ else if ( !m_bToBeEncrypted && m_xBaseEncryptionData.is() )
+ m_xBaseEncryptionData.clear();
+ }
+ void SetPackageMember (bool bNewValue);
+
+ void setInitialisationVector (const css::uno::Sequence < sal_Int8 >& rNewVector )
+ { m_xBaseEncryptionData->m_aInitVector = rNewVector;}
+ void setSalt (const css::uno::Sequence < sal_Int8 >& rNewSalt )
+ { m_xBaseEncryptionData->m_aSalt = rNewSalt;}
+ void setDigest (const css::uno::Sequence < sal_Int8 >& rNewDigest )
+ { m_xBaseEncryptionData->m_aDigest = rNewDigest;}
+ void setIterationCount(::std::optional<sal_Int32> const oNewCount)
+ {
+ m_xBaseEncryptionData->m_oPBKDFIterationCount = oNewCount;
+ }
+ void setArgon2Args(::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args)
+ {
+ m_xBaseEncryptionData->m_oArgon2Args = oArgon2Args;
+ }
+ void setSize (const sal_Int64 nNewSize);
+
+ ZipPackageStream( ZipPackage & rNewPackage,
+ const css::uno::Reference < css::uno::XComponentContext >& xContext,
+ sal_Int32 nFormat,
+ bool bAllowRemoveOnInsert );
+ virtual ~ZipPackageStream() override;
+
+ css::uno::Reference< css::io::XInputStream > GetRawEncrStreamNoHeaderCopy();
+ css::uno::Reference< css::io::XInputStream > TryToGetRawFromDataStream(bool bAddHeaderForEncr );
+
+ bool ParsePackageRawStream();
+ virtual bool saveChild( const OUString &rPath,
+ std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const css::uno::Sequence < sal_Int8 >& rEncryptionKey,
+ ::std::optional<sal_Int32> oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args,
+ const rtlRandomPool &rRandomPool ) override;
+
+ void setZipEntryOnLoading( const ZipEntry &rInEntry);
+ void successfullyWritten( ZipEntry const *pEntry );
+
+ // XActiveDataSink
+ virtual void SAL_CALL setInputStream( const css::uno::Reference< css::io::XInputStream >& aStream ) override;
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getInputStream( ) override;
+
+ // XDataSinkEncrSupport
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getDataStream() override;
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getRawStream() override;
+ virtual void SAL_CALL setDataStream(
+ const css::uno::Reference< css::io::XInputStream >& aStream ) override;
+ virtual void SAL_CALL setRawStream(
+ const css::uno::Reference< css::io::XInputStream >& aStream ) override;
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getPlainRawStream() override;
+
+ // XPropertySet
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/pch/precompiled_package2.cxx b/package/inc/pch/precompiled_package2.cxx
new file mode 100644
index 0000000000..63e41961d6
--- /dev/null
+++ b/package/inc/pch/precompiled_package2.cxx
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "precompiled_package2.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/pch/precompiled_package2.hxx b/package/inc/pch/precompiled_package2.hxx
new file mode 100644
index 0000000000..46d0b427c9
--- /dev/null
+++ b/package/inc/pch/precompiled_package2.hxx
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ This file has been autogenerated by update_pch.sh. It is possible to edit it
+ manually (such as when an include file has been moved/renamed/removed). All such
+ manual changes will be rewritten by the next run of update_pch.sh (which presumably
+ also fixes all possible problems, so it's usually better to use it).
+
+ Generated on 2021-04-08 13:55:58 using:
+ ./bin/update_pch package package2 --cutoff=3 --exclude:system --include:module --include:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./package/inc/pch/precompiled_package2.hxx "make package.build" --find-conflicts
+*/
+
+#include <sal/config.h>
+#if PCH_LEVEL >= 1
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <cmath>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <initializer_list>
+#include <iomanip>
+#include <limits>
+#include <math.h>
+#include <memory>
+#include <new>
+#include <ostream>
+#include <stddef.h>
+#include <string.h>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+#include <zlib.h>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/diagnose.h>
+#include <osl/diagnose.hxx>
+#include <osl/doublecheckedlocking.h>
+#include <osl/endian.h>
+#include <osl/file.h>
+#include <osl/getglobalmutex.hxx>
+#include <osl/interlck.h>
+#include <osl/mutex.h>
+#include <osl/mutex.hxx>
+#include <osl/thread.hxx>
+#include <osl/time.h>
+#include <rtl/alloc.h>
+#include <rtl/cipher.h>
+#include <rtl/crc.h>
+#include <rtl/digest.h>
+#include <rtl/instance.hxx>
+#include <rtl/math.h>
+#include <rtl/random.h>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.h>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/stringconcat.hxx>
+#include <rtl/stringutils.hxx>
+#include <rtl/textcvt.h>
+#include <rtl/textenc.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <sal/saldllapi.h>
+#include <sal/types.h>
+#include <sal/typesizes.h>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <basegfx/basegfxdllapi.h>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/tuple/b3dtuple.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Type.h>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/TypeClass.hdl>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/uno/XWeak.hpp>
+#include <com/sun/star/uno/genfunc.h>
+#include <com/sun/star/uno/genfunc.hxx>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <comphelper/comphelperdllapi.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/refcountedmutex.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <cppu/cppudllapi.h>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/cppuhelperdllapi.h>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/implbase_ex.hxx>
+#include <cppuhelper/implbase_ex_post.hxx>
+#include <cppuhelper/implbase_ex_pre.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/weak.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/underlyingenumvalue.hxx>
+#include <salhelper/salhelperdllapi.h>
+#include <salhelper/simplereferenceobject.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/toolsdllapi.h>
+#include <typelib/typeclass.h>
+#include <typelib/typedescription.h>
+#include <typelib/uik.h>
+#include <uno/any2.h>
+#include <uno/data.h>
+#include <uno/sequence2.h>
+#include <unotools/options.hxx>
+#include <unotools/unotoolsdllapi.h>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#include <CRC32.hxx>
+#include <EncryptedDataHeader.hxx>
+#include <EncryptionData.hxx>
+#include <HashMaps.hxx>
+#include <PackageConstants.hxx>
+#include <ThreadedDeflater.hxx>
+#include <ZipEntry.hxx>
+#include <ZipEnumeration.hxx>
+#include <ZipFile.hxx>
+#include <ZipOutputEntry.hxx>
+#include <ZipOutputStream.hxx>
+#include <ZipPackageBuffer.hxx>
+#include <ZipPackageFolder.hxx>
+#include <ZipPackageStream.hxx>
+#include <package/packagedllapi.hxx>
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/pch/precompiled_xstor.cxx b/package/inc/pch/precompiled_xstor.cxx
new file mode 100644
index 0000000000..10f1d920d2
--- /dev/null
+++ b/package/inc/pch/precompiled_xstor.cxx
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "precompiled_xstor.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/pch/precompiled_xstor.hxx b/package/inc/pch/precompiled_xstor.hxx
new file mode 100644
index 0000000000..9b5e894a1e
--- /dev/null
+++ b/package/inc/pch/precompiled_xstor.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ This file has been autogenerated by update_pch.sh. It is possible to edit it
+ manually (such as when an include file has been moved/renamed/removed). All such
+ manual changes will be rewritten by the next run of update_pch.sh (which presumably
+ also fixes all possible problems, so it's usually better to use it).
+
+ Generated on 2021-03-08 13:14:06 using:
+ ./bin/update_pch package xstor --cutoff=2 --exclude:system --include:module --exclude:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./package/inc/pch/precompiled_xstor.hxx "make package.build" --find-conflicts
+*/
+
+#include <sal/config.h>
+#if PCH_LEVEL >= 1
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <iomanip>
+#include <limits>
+#include <memory>
+#include <new>
+#include <ostream>
+#include <stddef.h>
+#include <string.h>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/diagnose.h>
+#include <osl/interlck.h>
+#include <osl/mutex.h>
+#include <osl/mutex.hxx>
+#include <rtl/alloc.h>
+#include <rtl/digest.h>
+#include <rtl/instance.hxx>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/stringconcat.hxx>
+#include <rtl/stringutils.hxx>
+#include <rtl/textcvt.h>
+#include <rtl/textenc.h>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <sal/saldllapi.h>
+#include <sal/types.h>
+#include <sal/typesizes.h>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Type.h>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/TypeClass.hdl>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/uno/XWeak.hpp>
+#include <com/sun/star/uno/genfunc.h>
+#include <com/sun/star/uno/genfunc.hxx>
+#include <comphelper/comphelperdllapi.h>
+#include <comphelper/ofopxmlhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <cppu/cppudllapi.h>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/cppuhelperdllapi.h>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <salhelper/salhelperdllapi.h>
+#include <salhelper/simplereferenceobject.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <typelib/typeclass.h>
+#include <typelib/typedescription.h>
+#include <typelib/uik.h>
+#include <uno/any2.h>
+#include <uno/data.h>
+#include <uno/sequence2.h>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/zipfileaccess.hxx b/package/inc/zipfileaccess.hxx
new file mode 100644
index 0000000000..b67a44b5c1
--- /dev/null
+++ b/package/inc/zipfileaccess.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_INC_ZIPFILEACCESS_HXX
+#define INCLUDED_PACKAGE_INC_ZIPFILEACCESS_HXX
+
+#include <com/sun/star/packages/zip/XZipFileAccess2.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/refcountedmutex.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include "ZipFile.hxx"
+
+#include <memory>
+#include <optional>
+
+class OZipFileAccess final : public ::cppu::WeakImplHelper<
+ css::packages::zip::XZipFileAccess2,
+ css::lang::XInitialization,
+ css::lang::XComponent,
+ css::lang::XServiceInfo >
+{
+ rtl::Reference<comphelper::RefCountedMutex> m_aMutexHolder;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::io::XInputStream > m_xContentStream;
+ std::optional<ZipFile> m_pZipFile;
+ std::unique_ptr<::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>> m_pListenersContainer;
+ bool m_bDisposed;
+ bool m_bOwnContent;
+
+public:
+ OZipFileAccess( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual ~OZipFileAccess() override;
+
+ static css::uno::Sequence< OUString > GetPatternsFromString_Impl( const OUString& aString );
+
+ static bool StringGoodForPattern_Impl( std::u16string_view,
+ const css::uno::Sequence< OUString >& aPattern );
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+
+ // XZipFileAccess
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getStreamByPattern( const OUString& aPattern ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose( ) override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/qa/cppunit/data/a2z.zip b/package/qa/cppunit/data/a2z.zip
new file mode 100644
index 0000000000..4a04508b8f
--- /dev/null
+++ b/package/qa/cppunit/data/a2z.zip
Binary files differ
diff --git a/package/qa/cppunit/data/export64.zip b/package/qa/cppunit/data/export64.zip
new file mode 100644
index 0000000000..b303266967
--- /dev/null
+++ b/package/qa/cppunit/data/export64.zip
Binary files differ
diff --git a/package/qa/cppunit/data/fail/.gitignore b/package/qa/cppunit/data/fail/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/package/qa/cppunit/data/fail/.gitignore
diff --git a/package/qa/cppunit/data/fail/EDB-29934-1.zip b/package/qa/cppunit/data/fail/EDB-29934-1.zip
new file mode 100644
index 0000000000..510e75eaa0
--- /dev/null
+++ b/package/qa/cppunit/data/fail/EDB-29934-1.zip
Binary files differ
diff --git a/package/qa/cppunit/data/indeterminate/.gitignore b/package/qa/cppunit/data/indeterminate/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/package/qa/cppunit/data/indeterminate/.gitignore
diff --git a/package/qa/cppunit/data/pass/.gitignore b/package/qa/cppunit/data/pass/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/package/qa/cppunit/data/pass/.gitignore
diff --git a/package/qa/cppunit/data/pass/ofz56826-1.zip b/package/qa/cppunit/data/pass/ofz56826-1.zip
new file mode 100644
index 0000000000..b9acfe34da
--- /dev/null
+++ b/package/qa/cppunit/data/pass/ofz56826-1.zip
Binary files differ
diff --git a/package/qa/cppunit/test_package.cxx b/package/qa/cppunit/test_package.cxx
new file mode 100644
index 0000000000..911e0ea603
--- /dev/null
+++ b/package/qa/cppunit/test_package.cxx
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <comphelper/processfactory.hxx>
+#include <unotest/filters-test.hxx>
+#include <unotest/bootstrapfixturebase.hxx>
+#include <comphelper/threadpool.hxx>
+#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+
+#include <iterator>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+ class PackageTest
+ : public test::FiltersTest
+ , public test::BootstrapFixtureBase
+ {
+ public:
+ PackageTest() {}
+
+ virtual void setUp() override;
+
+ virtual bool load(const OUString &,
+ const OUString &rURL, const OUString &,
+ SfxFilterFlags, SotClipboardFormatId, unsigned int) override;
+
+ void test();
+ void testThreadedStreams();
+ void testBufferedThreadedStreams();
+ void testZip64();
+
+ CPPUNIT_TEST_SUITE(PackageTest);
+ CPPUNIT_TEST(test);
+ CPPUNIT_TEST(testThreadedStreams);
+ CPPUNIT_TEST(testBufferedThreadedStreams);
+ CPPUNIT_TEST(testZip64);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ uno::Reference<container::XNameAccess> mxNA;
+ void verifyStreams( std::vector<std::vector<char>> &aBuffers );
+ };
+
+ void PackageTest::setUp()
+ {
+ BootstrapFixtureBase::setUp();
+ OUString aURL = m_directories.getURLFromSrc(u"/package/qa/cppunit/data/a2z.zip");
+
+ uno::Sequence<beans::NamedValue> aNVs{ { "URL", uno::Any(aURL) } };
+ uno::Sequence<uno::Any> aArgs{ uno::Any(aNVs) };
+
+ uno::Reference<uno::XComponentContext> xCxt = comphelper::getProcessComponentContext();
+ uno::Reference<lang::XMultiComponentFactory> xSvcMgr = xCxt->getServiceManager();
+
+ uno::Reference<packages::zip::XZipFileAccess2> xZip(
+ xSvcMgr->createInstanceWithArgumentsAndContext(
+ "com.sun.star.packages.zip.ZipFileAccess", aArgs, xCxt),
+ uno::UNO_QUERY);
+
+ CPPUNIT_ASSERT(xZip.is());
+
+ mxNA = xZip;
+ CPPUNIT_ASSERT(mxNA.is());
+ }
+
+ bool PackageTest::load(const OUString &,
+ const OUString &rURL, const OUString &,
+ SfxFilterFlags, SotClipboardFormatId, unsigned int)
+ {
+ try
+ {
+ uno::Reference<css::packages::zip::XZipFileAccess2> xZip(
+ css::packages::zip::ZipFileAccess::createWithURL(comphelper::getProcessComponentContext(), rURL));
+ return xZip.is();
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+ void PackageTest::test()
+ {
+ testDir(OUString(),
+ m_directories.getURLFromSrc(u"/package/qa/cppunit/data/"));
+ }
+
+ void PackageTest::verifyStreams( std::vector<std::vector<char>> &aBuffers )
+ {
+ CPPUNIT_ASSERT_EQUAL(size_t(26), aBuffers.size());
+ auto itBuf = aBuffers.begin();
+
+ for (char c = 'a'; c <= 'z'; ++c, ++itBuf)
+ {
+ const std::vector<char>& rBuf = *itBuf;
+ CPPUNIT_ASSERT_EQUAL(size_t(1048576), rBuf.size()); // 1 MB each.
+ for (char check : rBuf)
+ if (c != check)
+ CPPUNIT_ASSERT_MESSAGE("stream does not contain expected data", false);
+ }
+ }
+
+ // TODO : This test currently doesn't fail even when you set
+ // UseBufferedStream to false. Look into this and replace it with a better
+ // test that actually fails when the aforementioned flag is set to false.
+ void PackageTest::testThreadedStreams()
+ {
+ class Worker : public comphelper::ThreadTask
+ {
+ uno::Reference<io::XInputStream> mxStrm;
+ std::vector<char>& mrBuf;
+
+ public:
+ Worker(
+ const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
+ const uno::Reference<io::XInputStream>& xStrm,
+ std::vector<char>& rBuf ) :
+ comphelper::ThreadTask(pTag), mxStrm(xStrm), mrBuf(rBuf) {}
+
+ virtual void doWork() override
+ {
+ sal_Int32 nSize = mxStrm->available();
+
+ uno::Sequence<sal_Int8> aBytes;
+ while (nSize > 0)
+ {
+ sal_Int32 nBytesRead = mxStrm->readBytes(aBytes, 4096);
+ const sal_Int8* p = aBytes.getArray();
+ const sal_Int8* pEnd = p + nBytesRead;
+ std::copy(p, pEnd, std::back_inserter(mrBuf));
+ nSize -= nBytesRead;
+ }
+ }
+ };
+
+ {
+ comphelper::ThreadPool aPool(4);
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ std::vector<std::vector<char>> aTestBuffers(26);
+ auto itBuf = aTestBuffers.begin();
+
+ for (char c = 'a'; c <= 'z'; ++c, ++itBuf)
+ {
+ OUString aName = OUStringChar(c) + ".txt";
+
+ uno::Reference<io::XInputStream> xStrm;
+ mxNA->getByName(aName) >>= xStrm;
+
+ CPPUNIT_ASSERT(xStrm.is());
+ aPool.pushTask(std::make_unique<Worker>(pTag, xStrm, *itBuf));
+ }
+
+ aPool.waitUntilDone(pTag);
+ verifyStreams( aTestBuffers );
+ }
+ }
+
+ void PackageTest::testBufferedThreadedStreams()
+ {
+ std::vector<std::vector<char>> aTestBuffers(26);
+ auto itBuf = aTestBuffers.begin();
+ sal_Int32 nReadSize = 0;
+
+ for (char c = 'a'; c <= 'z'; ++c, ++itBuf)
+ {
+ itBuf->reserve(1024*1024);
+ OUString aName = OUStringChar(c) + ".txt";
+
+ uno::Reference<io::XInputStream> xStrm;
+ //Size of each stream is 1mb (>10000) => XBufferedThreadedStream
+ mxNA->getByName(aName) >>= xStrm;
+
+ CPPUNIT_ASSERT(xStrm.is());
+ sal_Int32 nSize = xStrm->available();
+
+ uno::Sequence<sal_Int8> aBytes;
+ //Read chunks of increasing size
+ nReadSize += 1024;
+
+ while (nSize > 0)
+ {
+ sal_Int32 nBytesRead = xStrm->readBytes(aBytes, nReadSize);
+ const sal_Int8* p = aBytes.getArray();
+ const sal_Int8* pEnd = p + nBytesRead;
+ std::copy(p, pEnd, std::back_inserter(*itBuf));
+ nSize -= nBytesRead;
+ }
+ }
+
+ verifyStreams( aTestBuffers );
+ }
+
+ void PackageTest::testZip64()
+ {
+ // This small zip file have 2 files (content.xml, styles.xml) that have
+ // Zip64 Extended Information Extra Field in both
+ // "Local file header" and "Central directory file header",
+ // and have ZIP64 format "Data descriptor".
+ OUString aURL2 = m_directories.getURLFromSrc(u"/package/qa/cppunit/data/export64.zip");
+
+ uno::Sequence<beans::NamedValue> aNVs2{ { "URL", uno::Any(aURL2) } };
+ uno::Sequence<uno::Any> aArgs2{ uno::Any(aNVs2) };
+
+ uno::Reference<uno::XComponentContext> xCxt = comphelper::getProcessComponentContext();
+ uno::Reference<lang::XMultiComponentFactory> xSvcMgr = xCxt->getServiceManager();
+
+ // Without Zip64 support, it would crash here
+ uno::Reference<packages::zip::XZipFileAccess2> xZip2(
+ xSvcMgr->createInstanceWithArgumentsAndContext(
+ "com.sun.star.packages.zip.ZipFileAccess", aArgs2, xCxt),
+ uno::UNO_QUERY);
+
+ CPPUNIT_ASSERT(xZip2.is());
+
+ uno::Reference<container::XNameAccess> xNA;
+ xNA = xZip2;
+ CPPUNIT_ASSERT(xNA.is());
+
+ // Check if the styles.xml seems to be right
+ uno::Reference<io::XInputStream> xStrm;
+ xNA->getByName("styles.xml") >>= xStrm;
+ CPPUNIT_ASSERT(xStrm.is());
+ // Filesize check
+ sal_Int32 nSize = xStrm->available();
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1112), nSize);
+
+ uno::Sequence<sal_Int8> aBytes;
+ sal_Int32 nBytesRead = xStrm->readBytes(aBytes, nSize);
+ const sal_Int8* p = aBytes.getArray();
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1112), nBytesRead);
+
+ // Check the uncompressed styles.xml file content.
+ OString aFile(static_cast<const char*>(static_cast<const void*>(p)), nSize);
+ CPPUNIT_ASSERT(aFile.startsWith(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<office:document-styles"));
+ CPPUNIT_ASSERT(aFile.endsWith(
+ "</number:time-style>\r\n </office:styles>\r\n</office:document-styles>\r\n"));
+ }
+
+ CPPUNIT_TEST_SUITE_REGISTRATION(PackageTest);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/qa/ofopxmlstorages/StorageTest.java b/package/qa/ofopxmlstorages/StorageTest.java
new file mode 100644
index 0000000000..5f3b2ff2a7
--- /dev/null
+++ b/package/qa/ofopxmlstorages/StorageTest.java
@@ -0,0 +1,7 @@
+package complex.ofopxmlstorages;
+
+public interface StorageTest
+{
+ boolean test();
+}
+
diff --git a/package/qa/ofopxmlstorages/StorageUnitTest.java b/package/qa/ofopxmlstorages/StorageUnitTest.java
new file mode 100644
index 0000000000..e8ee7696a6
--- /dev/null
+++ b/package/qa/ofopxmlstorages/StorageUnitTest.java
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package complex.ofopxmlstorages;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XMultiComponentFactory;
+import com.sun.star.connection.XConnector;
+import com.sun.star.connection.XConnection;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.uno.XNamingService;
+import com.sun.star.uno.XComponentContext;
+
+import com.sun.star.container.*;
+import com.sun.star.beans.*;
+import com.sun.star.lang.*;
+
+import complexlib.ComplexTestCase;
+
+import complex.ofopxmlstorages.*;
+
+import util.utils;
+import java.util.*;
+import java.io.*;
+
+/* This unit test for storage objects is designed to
+ * test most important statements from storage service
+ * specification.
+ *
+ * Regression tests are added to extend the tested
+ * functionalities.
+ */
+public class StorageUnitTest extends ComplexTestCase
+{
+ private XMultiServiceFactory m_xMSF = null;
+ private XSingleServiceFactory m_xStorageFactory = null;
+
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "ExecuteTest01",
+ "ExecuteTest02",
+ "ExecuteTest03",
+ "ExecuteTest04",
+ "ExecuteTest05",
+ "ExecuteTest06",
+ "ExecuteTest07",
+ "ExecuteTest08"
+ };
+ }
+
+ public String getTestObjectName()
+ {
+ return "StorageUnitTest";
+ }
+
+ public void before()
+ {
+ m_xMSF = (XMultiServiceFactory)param.getMSF();
+ if ( m_xMSF == null )
+ {
+ failed( "Can't create service factory!" );
+ return;
+ }
+
+ try {
+ Object oStorageFactory = m_xMSF.createInstance( "com.sun.star.embed.StorageFactory" );
+ m_xStorageFactory = (XSingleServiceFactory)UnoRuntime.queryInterface( XSingleServiceFactory.class,
+ oStorageFactory );
+ }
+ catch( Exception e )
+ {
+ failed( "Can't create storage factory!" );
+ return;
+ }
+
+ if ( m_xStorageFactory == null )
+ {
+ failed( "Can't create service factory!" );
+ return;
+ }
+ }
+
+ public void ExecuteTest01()
+ {
+ StorageTest aTest = new Test01( m_xMSF, m_xStorageFactory, log );
+ assure( "Test01 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest02()
+ {
+ StorageTest aTest = new Test02( m_xMSF, m_xStorageFactory, log );
+ assure( "Test02 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest03()
+ {
+ StorageTest aTest = new Test03( m_xMSF, m_xStorageFactory, log );
+ assure( "Test03 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest04()
+ {
+ StorageTest aTest = new Test04( m_xMSF, m_xStorageFactory, log );
+ assure( "Test04 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest05()
+ {
+ StorageTest aTest = new Test05( m_xMSF, m_xStorageFactory, log );
+ assure( "Test05 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest06()
+ {
+ StorageTest aTest = new Test06( m_xMSF, m_xStorageFactory, log );
+ assure( "Test06 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest07()
+ {
+ StorageTest aTest = new Test07( m_xMSF, m_xStorageFactory, log );
+ assure( "Test07 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest08()
+ {
+ StorageTest aTest = new Test08( m_xMSF, m_xStorageFactory, log );
+ assure( "Test08 failed!", aTest.test() );
+ }
+}
+
diff --git a/package/qa/ofopxmlstorages/Test01.java b/package/qa/ofopxmlstorages/Test01.java
new file mode 100644
index 0000000000..dd8665bc54
--- /dev/null
+++ b/package/qa/ofopxmlstorages/Test01.java
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.ofopxmlstorages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+import com.sun.star.beans.StringPair;
+
+import share.LogWriter;
+import complex.ofopxmlstorages.TestHelper;
+import complex.ofopxmlstorages.StorageTest;
+
+public class Test01 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test01( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test01: " );
+ }
+
+ public boolean test()
+ {
+ StringPair[][] aRelations1 =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue1" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value1" ) }
+ };
+
+ StringPair[][] aRelations2 =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue2" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown2" ), new StringPair( "Target", "URL value 2" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown2" ), new StringPair( "Target", "URL value 2" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) }
+ };
+
+ try
+ {
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ XStorage xTempStorage = m_aTestHelper.createTempStorage( m_xMSF, m_xStorageFactory );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage,
+ "SubStream1",
+ "MediaType1",
+ true,
+ pBytes1,
+ aRelations1 ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage,
+ "SubStream2",
+ "MediaType2",
+ false,
+ pBytes2,
+ aRelations2 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ true,
+ ElementModes.WRITE,
+ aRelations1 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ false,
+ ElementModes.WRITE,
+ aRelations1 ) )
+ return false;
+
+ // create temporary storage based on a previously created temporary file
+ XStorage xTempFileStorage = m_aTestHelper.createStorageFromURL( m_xStorageFactory,
+ sTempFileURL,
+ ElementModes.WRITE );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // copy xTempStorage to xTempFileStorage
+ // xTempFileStorage will be automatically committed
+ if ( !m_aTestHelper.copyStorage( xTempStorage, xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) || !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now check all the written and copied information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ XStorage xResultStorage = m_aTestHelper.createStorageFromURL( m_xStorageFactory,
+ sTempFileURL,
+ ElementModes.WRITE );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage,
+ true,
+ ElementModes.WRITE,
+ aRelations1 ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage,
+ false,
+ ElementModes.READ,
+ aRelations1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage,
+ "SubStream1",
+ "MediaType1",
+ pBytes1,
+ aRelations1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage,
+ "SubStream2",
+ "MediaType2",
+ pBytes2,
+ aRelations2 ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/ofopxmlstorages/Test02.java b/package/qa/ofopxmlstorages/Test02.java
new file mode 100644
index 0000000000..a39e78ca47
--- /dev/null
+++ b/package/qa/ofopxmlstorages/Test02.java
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.ofopxmlstorages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+import com.sun.star.beans.StringPair;
+
+import share.LogWriter;
+import complex.ofopxmlstorages.TestHelper;
+import complex.ofopxmlstorages.StorageTest;
+
+public class Test02 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test02( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test02: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ StringPair[][] aRelations =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) }
+ };
+
+
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ XStorage xTempStorage = m_aTestHelper.createStorageFromStream( m_xStorageFactory,
+ xTempFileStream,
+ ElementModes.WRITE );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage,
+ "SubStream1",
+ "MediaType1",
+ true,
+ pBytes1,
+ aRelations ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ true,
+ ElementModes.WRITE,
+ aRelations ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ false,
+ ElementModes.WRITE,
+ aRelations ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+
+ // now check all the written information
+
+
+ // close the output part of the temporary stream
+ // the output part must present since we already wrote to the stream
+ if ( !m_aTestHelper.closeOutput( xTempFileStream ) )
+ return false;
+
+ XInputStream xTempInStream = m_aTestHelper.getInputStream( xTempFileStream );
+ if ( xTempInStream == null )
+ return false;
+
+
+ // open input stream
+ XStorage xResultStorage = m_aTestHelper.createStorageFromInputStream( m_xStorageFactory, xTempInStream );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open storage based on input stream!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, true, ElementModes.READ, aRelations ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage,
+ false,
+ ElementModes.READ,
+ aRelations ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream1", "MediaType1", pBytes1, aRelations ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/ofopxmlstorages/Test03.java b/package/qa/ofopxmlstorages/Test03.java
new file mode 100644
index 0000000000..d2f7bfa5b3
--- /dev/null
+++ b/package/qa/ofopxmlstorages/Test03.java
@@ -0,0 +1,251 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.ofopxmlstorages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+import com.sun.star.container.XNameAccess;
+import com.sun.star.beans.StringPair;
+
+import share.LogWriter;
+import complex.ofopxmlstorages.TestHelper;
+import complex.ofopxmlstorages.StorageTest;
+
+public class Test03 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test03( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test03: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ StringPair[][] aRelations =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) }
+ };
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ XStorage xTempStorage = m_aTestHelper.createTempStorage( m_xMSF, m_xStorageFactory );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage,
+ "SubStream1",
+ "MediaType1",
+ true,
+ pBytes1,
+ aRelations ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage,
+ "SubStream2",
+ "MediaType2",
+ false,
+ pBytes2,
+ aRelations ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ false,
+ ElementModes.WRITE,
+ aRelations ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+
+ // check storage hierarchy tree
+
+
+ // check that isStorageElement() and isStreamElement reacts to nonexistent object correctly
+ try {
+ xTempStorage.isStorageElement( "does not exist" );
+ m_aTestHelper.Error( "Nonexistent element doesn't detected by isStorageElement() call!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by isStorageElement() call: " + e );
+ return false;
+ }
+
+ try {
+ xTempStorage.isStreamElement( "does not exist" );
+ m_aTestHelper.Error( "Nonexistent element doesn't detected by isStreamElement() call!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by isStreamElement() call: " + e );
+ return false;
+ }
+
+ XNameAccess xRootNameAccess = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xTempStorage );
+ if ( xRootNameAccess == null )
+ {
+ m_aTestHelper.Error( "Root storage doesn't support XNameAccess!" );
+ return false;
+ }
+
+ try {
+ if ( !xTempStorage.isStorageElement( "SubStorage1" ) || xTempStorage.isStreamElement( "SubStorage1" ) )
+ {
+ m_aTestHelper.Error( "Child 'SubStorage1' can not be detected as storage!" );
+ return false;
+ }
+
+ if ( xTempStorage.isStorageElement( "SubStream1" ) || !xTempStorage.isStreamElement( "SubStream1" ) )
+ {
+ m_aTestHelper.Error( "Child 'SubStream1' can not be detected as stream!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Child's type can not be detected, exception: " + e );
+ return false;
+ }
+
+
+ // check that root storage contents are represented correctly
+ String sRootCont[] = xRootNameAccess.getElementNames();
+
+ if ( sRootCont.length != 2 )
+ {
+ m_aTestHelper.Error( "Root storage contains wrong amount of children!" );
+ return false;
+ }
+
+ if ( !( sRootCont[0].equals( "SubStorage1" ) && sRootCont[1].equals( "SubStream1" )
+ || sRootCont[0].equals( "SubStream1" ) && sRootCont[1].equals( "SubStorage1" ) )
+ || !( xRootNameAccess.hasByName( "SubStream1" ) && xRootNameAccess.hasByName( "SubStorage1" ) ) )
+ {
+ m_aTestHelper.Error( "Root storage contains wrong list of children!" );
+ return false;
+ }
+
+ // get storage through XNameAccess
+ XStorage xResultSubStorage = getStorageFromNameAccess( xRootNameAccess, "SubStorage1" );
+ if ( xResultSubStorage == null )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage,
+ false,
+ ElementModes.READ,
+ aRelations ) )
+ return false;
+
+ XNameAccess xChildAccess = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xResultSubStorage );
+ if ( xChildAccess == null )
+ {
+ m_aTestHelper.Error( "Child storage doesn't support XNameAccess!" );
+ return false;
+ }
+
+ if ( !xChildAccess.hasByName( "SubStream2" )
+ || !xResultSubStorage.isStreamElement( "SubStream2" )
+ || xResultSubStorage.isStorageElement( "SubStream2" ) )
+ {
+ m_aTestHelper.Error( "'SubStream2' can not be detected as child stream element of 'SubStorage1'!" );
+ return false;
+ }
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+ public XStorage getStorageFromNameAccess( XNameAccess xAccess, String sName )
+ {
+ try
+ {
+ Object oStorage = xAccess.getByName( sName );
+ XStorage xResult = (XStorage) UnoRuntime.queryInterface( XStorage.class, oStorage );
+
+ if ( xResult != null )
+ return xResult;
+ else
+ m_aTestHelper.Error( "Can't retrieve substorage '" + sName + "' through XNameAccess!" );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can't retrieve substorage '" + sName + "' through XNameAccess, exception: " + e );
+ }
+
+ return null;
+ }
+
+}
+
diff --git a/package/qa/ofopxmlstorages/Test04.java b/package/qa/ofopxmlstorages/Test04.java
new file mode 100644
index 0000000000..7bc793a603
--- /dev/null
+++ b/package/qa/ofopxmlstorages/Test04.java
@@ -0,0 +1,326 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.ofopxmlstorages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+import com.sun.star.lang.DisposedException;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.container.XNameAccess;
+
+import com.sun.star.embed.*;
+import com.sun.star.beans.StringPair;
+
+import share.LogWriter;
+import complex.ofopxmlstorages.TestHelper;
+import complex.ofopxmlstorages.StorageTest;
+
+public class Test04 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test04( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test04: " );
+ }
+
+ public boolean test()
+ {
+ StringPair[][] aRelations1 =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue1" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value1" ) }
+ };
+
+ StringPair[][] aRelations2 =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue2" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown2" ), new StringPair( "Target", "URL value 2" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown2" ), new StringPair( "Target", "URL value 2" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) }
+ };
+
+ try
+ {
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ XStorage xTempStorage = m_aTestHelper.createTempStorage( m_xMSF, m_xStorageFactory );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open substorages and create streams there
+
+ // first substorage of the root storage
+ XStorage xTempSubStorage1 = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage1 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage1,
+ "SubStream1",
+ "MediaType1",
+ true,
+ pBytes1,
+ aRelations1 ) )
+ return false;
+
+ // second substorage of the root storage
+ XStorage xTempSubStorage2 = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage2",
+ ElementModes.WRITE );
+ if ( xTempSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage2,
+ "SubStream2",
+ "MediaType2",
+ false,
+ pBytes2,
+ aRelations2 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ true,
+ ElementModes.WRITE,
+ aRelations2 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage1,
+ false,
+ ElementModes.WRITE,
+ aRelations2 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage2,
+ false,
+ ElementModes.WRITE,
+ aRelations2 ) )
+ return false;
+
+ // create temporary storage based on a previously created temporary file
+ XStorage xTempFileStorage = m_aTestHelper.createStorageFromURL( m_xStorageFactory,
+ sTempFileURL,
+ ElementModes.WRITE );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.copyElementTo( xTempStorage, "SubStorage1", xTempFileStorage ) )
+ return false;
+
+ // if storage is not committed before disposing all the changes will be lost
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage2 ) )
+ return false;
+
+ // a storage must be disposed before moving/removing otherwise the access will be denied
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage2 ) )
+ return false;
+
+ if ( !m_aTestHelper.moveElementTo( xTempStorage, "SubStorage2", xTempFileStorage ) )
+ return false;
+
+ // SubStorage2 must be removed and disposed now
+ try
+ {
+ xTempSubStorage2.isStreamElement( "SubStream2" );
+ m_aTestHelper.Error( "SubStorage2 must be disposed already!" );
+ return false;
+ }
+ catch( com.sun.star.lang.DisposedException de )
+ {
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception in case of disposed storage, exception: " + e );
+ return false;
+ }
+
+ if ( !m_aTestHelper.copyElementTo( xTempSubStorage1, "SubStream1", xTempFileStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.renameElement( xTempFileStorage, "SubStream1", "SubStream1_copy" ) )
+ return false;
+
+ if ( !m_aTestHelper.moveElementTo( xTempSubStorage1, "SubStream1", xTempFileStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) || !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now check all the written and copied information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ XStorage xResStorage = m_aTestHelper.createStorageFromURL( m_xStorageFactory,
+ sTempFileURL,
+ ElementModes.WRITE );
+
+ if ( xResStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ // open and check SubStorage1
+ XStorage xResSubStorage1 = m_aTestHelper.openSubStorage( xResStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResSubStorage1 == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResSubStorage1,
+ false,
+ ElementModes.READ,
+ aRelations2 ) )
+ return false;
+
+
+ // open and check SubStorage2
+ XStorage xResSubStorage2 = m_aTestHelper.openSubStorage( xResStorage,
+ "SubStorage2",
+ ElementModes.READ );
+ if ( xResSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResSubStorage2,
+ false,
+ ElementModes.READ,
+ aRelations2 ) )
+ return false;
+
+
+ // check all the result streams
+
+ if ( !m_aTestHelper.checkStream( xResStorage, "SubStream1", "MediaType1", pBytes1, aRelations1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResStorage, "SubStream1_copy", "MediaType1", pBytes1, aRelations1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubStorage1, "SubStream1", "MediaType1", pBytes1, aRelations1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubStorage2, "SubStream2", "MediaType2", pBytes2, aRelations2 ) )
+ return false;
+
+ // the storage must be disposed before removing
+ if ( !m_aTestHelper.disposeStorage( xResSubStorage2 ) )
+ return false;
+
+ // remove element and check that it was removed completely
+ if ( !m_aTestHelper.removeElement( xResStorage, "SubStorage2" ) )
+ return false;
+
+ try
+ {
+ XNameAccess xResAccess = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xResStorage );
+ if ( xResAccess.hasByName( "SubStorage2" ) )
+ m_aTestHelper.Error( "SubStorage2 must be removed already!" );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can't get access to root storage, exception: " + e );
+ return false;
+ }
+
+ try
+ {
+ xResSubStorage2.isStreamElement( "SubStream2" );
+
+ m_aTestHelper.Error( "SubStorage2 must be disposed already!" );
+ return false;
+ }
+ catch( com.sun.star.lang.DisposedException de )
+ {
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception in case of disposed storage, exception: " + e );
+ return false;
+ }
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/ofopxmlstorages/Test05.java b/package/qa/ofopxmlstorages/Test05.java
new file mode 100644
index 0000000000..1ed0ba4029
--- /dev/null
+++ b/package/qa/ofopxmlstorages/Test05.java
@@ -0,0 +1,332 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.ofopxmlstorages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+
+import com.sun.star.embed.*;
+import com.sun.star.beans.StringPair;
+
+import share.LogWriter;
+import complex.ofopxmlstorages.TestHelper;
+import complex.ofopxmlstorages.StorageTest;
+
+public class Test05 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test05( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test05: " );
+ }
+
+ public boolean test()
+ {
+ StringPair[][] aRelations1 =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue1" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value1" ) }
+ };
+
+ StringPair[][] aRelations2 =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue2" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown2" ), new StringPair( "Target", "URL value 2" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown2" ), new StringPair( "Target", "URL value 2" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) }
+ };
+
+ try
+ {
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on a previously created temporary file
+ XStorage xTempFileStorage = m_aTestHelper.createStorageFromURL( m_xStorageFactory,
+ sTempFileURL,
+ ElementModes.WRITE );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempFileStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage1",
+ ElementModes.WRITE );
+ if ( xSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xSubSubStorage,
+ "SubStream1",
+ "MediaType1",
+ true,
+ pBytes1,
+ aRelations1 ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xSubSubStorage,
+ "SubStream2",
+ "MediaType2",
+ false,
+ pBytes2,
+ aRelations2 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempFileStorage,
+ true,
+ ElementModes.WRITE,
+ aRelations2 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ false,
+ ElementModes.WRITE,
+ aRelations2 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xSubSubStorage,
+ false,
+ ElementModes.WRITE,
+ aRelations2 ) )
+ return false;
+
+
+ // commit all the storages
+ if ( !m_aTestHelper.commitStorage( xSubSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // try to open an opened substorage, open call must fail
+ if ( !m_aTestHelper.cantOpenStorage( xTempFileStorage, "SubStorage1" ) )
+ return false;
+
+
+ // reopen created streams
+ XStream xSubStream1 = m_aTestHelper.OpenStream( xSubSubStorage,
+ "SubStream1",
+ ElementModes.WRITE | ElementModes.NOCREATE );
+ XStream xSubStream2 = m_aTestHelper.OpenStream( xSubSubStorage,
+ "SubStream2",
+ ElementModes.READ | ElementModes.NOCREATE );
+ if ( xSubStream1 == null || xSubStream2 == null )
+ return false;
+
+ // it should be possible to have more than one copy of stream for reading
+ XStream xSubStream2clone = m_aTestHelper.OpenStream( xSubSubStorage,
+ "SubStream2",
+ ElementModes.READ | ElementModes.NOCREATE );
+ if ( xSubStream2 == null )
+ return false;
+
+
+ // so now the first stream can not be open neither for reading nor for writing
+ if ( !m_aTestHelper.cantOpenStream( xSubSubStorage, "SubStream1", ElementModes.WRITE )
+ || !m_aTestHelper.cantOpenStream( xSubSubStorage, "SubStream1", ElementModes.READ ) )
+ return false;
+
+ // the second stream can not be open for writing
+ if ( !m_aTestHelper.cantOpenStream( xSubSubStorage, "SubStream2", ElementModes.WRITE ) )
+ return false;
+
+
+ // dispose xTestSubStorage, all the subtree must be disposed
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // check that subtree was disposed correctly
+ try
+ {
+ xSubSubStorage.isStreamElement( "SubStream1" );
+ m_aTestHelper.Error( "Substorage was not disposed!" );
+ return false;
+ }
+ catch ( com.sun.star.lang.DisposedException de )
+ {}
+ catch ( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by disposed storage: " + e );
+ return false;
+ }
+
+ try
+ {
+ xSubStream1.getInputStream();
+ m_aTestHelper.Error( "Writeable substream was not disposed!" );
+ return false;
+ }
+ catch ( com.sun.star.lang.DisposedException de )
+ {}
+ catch ( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by disposed stream: " + e );
+ return false;
+ }
+
+ try
+ {
+ xSubStream2.getInputStream();
+ m_aTestHelper.Error( "Readonly substream was not disposed!" );
+ return false;
+ }
+ catch ( com.sun.star.lang.DisposedException de )
+ {}
+ catch ( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by disposed stream: " + e );
+ return false;
+ }
+
+
+ // dispose root storage
+ if ( !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+
+ // now check all the written and copied information
+
+
+ XStorage xResultStorage = m_aTestHelper.createStorageFromURL( m_xStorageFactory,
+ sTempFileURL,
+ ElementModes.READ );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage,
+ true,
+ ElementModes.READ,
+ aRelations2 ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage 'SubSubStorage'!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResSubStorage,
+ false,
+ ElementModes.READ,
+ aRelations2 ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResSubSubStorage = m_aTestHelper.openSubStorage( xResSubStorage,
+ "SubSubStorage1",
+ ElementModes.READ );
+ if ( xResSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage 'SubSubStorage'!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResSubSubStorage,
+ false,
+ ElementModes.READ,
+ aRelations2 ) )
+ return false;
+
+ // check substreams
+ if ( !m_aTestHelper.checkStream( xResSubSubStorage,
+ "SubStream1",
+ "MediaType1",
+ pBytes1,
+ aRelations1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubSubStorage,
+ "SubStream2",
+ "MediaType2",
+ pBytes2,
+ aRelations2 ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/ofopxmlstorages/Test06.java b/package/qa/ofopxmlstorages/Test06.java
new file mode 100644
index 0000000000..b0b6b7bcc7
--- /dev/null
+++ b/package/qa/ofopxmlstorages/Test06.java
@@ -0,0 +1,295 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.ofopxmlstorages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.lang.IllegalArgumentException;
+import com.sun.star.container.NoSuchElementException;
+import com.sun.star.container.ElementExistException;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.ofopxmlstorages.TestHelper;
+import complex.ofopxmlstorages.StorageTest;
+
+public class Test06 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test06( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test06: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ XStorage xTempStorage = m_aTestHelper.createTempStorage( m_xMSF, m_xStorageFactory );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ try
+ {
+ xTempStorage.copyToStorage( null );
+ m_aTestHelper.Error( "The method must throw an exception because of illegal parameter!" );
+ return false;
+ }
+ catch( com.sun.star.lang.IllegalArgumentException iae )
+ {}
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception because of illegal parameter : " + e );
+ return false;
+ }
+
+ // open new substorages
+ XStorage xTempSubStorage1 = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ XStorage xTempSubStorage2 = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage2",
+ ElementModes.WRITE );
+ if ( xTempSubStorage1 == null || xTempSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // in case stream is open for reading it must exist
+ try
+ {
+ xTempSubStorage1.openStreamElement( "NonExistingStream", ElementModes.READ );
+ m_aTestHelper.Error( "The method must throw an exception in case of try to open nonexistent stream for reading!" );
+ return false;
+ }
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to open nonexistent stream for reading : " + e );
+ return false;
+ }
+
+ // in case a storage is open for reading it must exist
+ try
+ {
+ xTempSubStorage1.openStreamElement( "NonExistingStorage", ElementModes.READ );
+ m_aTestHelper.Error( "The method must throw an exception in case of try to open nonexistent storage for reading!" );
+ return false;
+ }
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to open nonexistent storage for reading : " + e );
+ return false;
+ }
+
+ // in case of removing nonexistent element an exception must be thrown
+ try
+ {
+ xTempSubStorage1.removeElement( "NonExistingElement" );
+ m_aTestHelper.Error( "An exception must be thrown in case of removing nonexistent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to remove nonexistent element : " + e );
+ return false;
+ }
+
+ // in case of renaming of nonexistent element an exception must be thrown
+ try
+ {
+ xTempSubStorage1.renameElement( "NonExistingElement", "NewName" );
+ m_aTestHelper.Error( "An exception must be thrown in case of renaming nonexistent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to rename nonexistent element : " + e );
+ return false;
+ }
+
+ // in case of renaming to a name of existent element an exception must be thrown
+ try
+ {
+ xTempStorage.renameElement( "SubStorage1", "SubStorage2" );
+ m_aTestHelper.Error( "An exception must be thrown in case of renaming to the name of existent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.ElementExistException ee )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to rename to the name of existent element : " + e );
+ return false;
+ }
+
+ // in case of copying target storage must be provided
+ try
+ {
+ xTempStorage.copyElementTo( "SubStorage1", null, "SubStorage1" );
+ m_aTestHelper.Error( "An exception must be thrown in case empty reference is provided as target for copying!" );
+ return false;
+ }
+ catch( com.sun.star.lang.IllegalArgumentException iae )
+ {}
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case empty reference is provided as target for copying : " + e );
+ return false;
+ }
+
+ // in case of moving target storage must be provided
+ try
+ {
+ xTempStorage.moveElementTo( "SubStorage1", null, "SubStorage1" );
+ m_aTestHelper.Error( "An exception must be thrown in case empty reference is provided as target for moving!" );
+ return false;
+ }
+ catch( com.sun.star.lang.IllegalArgumentException iae )
+ {}
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case empty reference is provided as target for moving : " + e );
+ return false;
+ }
+
+
+ // prepare target for further testings
+
+ // create new temporary storage based on arbitrary medium
+ XStorage xTargetStorage = m_aTestHelper.createTempStorage( m_xMSF, m_xStorageFactory );
+ if ( xTargetStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTargetSubStorage = m_aTestHelper.openSubStorage( xTargetStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTargetSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // in case of copying of nonexistent element an exception must be thrown
+ try
+ {
+ xTempStorage.copyElementTo( "Nonexistent element", xTargetStorage, "Target" );
+ m_aTestHelper.Error( "An exception must be thrown in case of copying of nonexistent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of copying of nonexistent element: " + e );
+ return false;
+ }
+
+ // in case of moving of nonexistent element an exception must be thrown
+ try
+ {
+ xTempStorage.moveElementTo( "Nonexistent element", xTargetStorage, "Target" );
+ m_aTestHelper.Error( "An exception must be thrown in case of moving of nonexistent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of moving of nonexistent element: " + e );
+ return false;
+ }
+
+ // in case target for copying already exists an exception must be thrown
+ try
+ {
+ xTempStorage.copyElementTo( "SubStorage1", xTargetStorage, "SubStorage1" );
+ m_aTestHelper.Error( "An exception must be thrown in case target for copying already exists!" );
+ return false;
+ }
+ catch( com.sun.star.container.ElementExistException ee )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case target for copying already exists: " + e );
+ return false;
+ }
+
+ // in case target for moving already exists an exception must be thrown
+ try
+ {
+ xTempStorage.moveElementTo( "SubStorage1", xTargetStorage, "SubStorage1" );
+ m_aTestHelper.Error( "An exception must be thrown in case target for moving already exists!" );
+ return false;
+ }
+ catch( com.sun.star.container.ElementExistException ee )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case target for moving already exists: " + e );
+ return false;
+ }
+
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/ofopxmlstorages/Test07.java b/package/qa/ofopxmlstorages/Test07.java
new file mode 100644
index 0000000000..54f9a80b2c
--- /dev/null
+++ b/package/qa/ofopxmlstorages/Test07.java
@@ -0,0 +1,276 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.ofopxmlstorages;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.container.XNameAccess;
+import com.sun.star.io.XStream;
+
+import com.sun.star.embed.*;
+import com.sun.star.beans.StringPair;
+
+import share.LogWriter;
+import complex.ofopxmlstorages.TestHelper;
+import complex.ofopxmlstorages.StorageTest;
+
+public class Test07 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test07( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test07: " );
+ }
+
+ public boolean test()
+ {
+ StringPair[][] aRelations1 =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue1" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value1" ) }
+ };
+
+ StringPair[][] aRelations2 =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue2" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown2" ), new StringPair( "Target", "URL value 2" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown2" ), new StringPair( "Target", "URL value 2" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal2" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown" ), new StringPair( "Target", "URL value" ) }
+ };
+
+ try
+ {
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ XStorage xTempStorage = m_aTestHelper.createTempStorage( m_xMSF, m_xStorageFactory );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage,
+ "SubStream1",
+ "MediaType1",
+ true,
+ pBytes1,
+ aRelations1 ) )
+ return false;
+
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage,
+ "SubStream2",
+ "MediaType2",
+ true,
+ pBytes2,
+ aRelations2 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ true,
+ ElementModes.WRITE,
+ aRelations2 ) )
+ return false;
+
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ false,
+ ElementModes.WRITE,
+ aRelations2 ) )
+ return false;
+
+
+ // check cloning at current state
+
+
+ // the new storage still was not committed so the clone must be empty
+ XStorage xClonedSubStorage = m_aTestHelper.cloneSubStorage( m_xMSF, m_xStorageFactory, xTempStorage, "SubStorage1" );
+
+ if ( xClonedSubStorage == null )
+ {
+ m_aTestHelper.Error( "The result of clone is empty!" );
+ return false;
+ }
+
+ XNameAccess xClonedNameAccess = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xClonedSubStorage );
+ if ( xClonedNameAccess == null )
+ {
+ m_aTestHelper.Error( "XNameAccess is not implemented by the clone!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xClonedSubStorage,
+ true,
+ ElementModes.WRITE,
+ new StringPair[0][0] ) )
+ return false;
+
+ if ( xClonedNameAccess.hasElements() )
+ {
+ m_aTestHelper.Error( "The new substorage still was not committed so it must be empty!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.disposeStorage( xClonedSubStorage ) )
+ return false;
+
+ xClonedSubStorage = null;
+ xClonedNameAccess = null;
+
+ // the new stream was opened, written and closed, that means flashed
+ // so the clone must contain all the information
+ XStream xClonedSubStream = m_aTestHelper.cloneSubStream( xTempStorage, "SubStream1" );
+ if ( !m_aTestHelper.InternalCheckStream( xClonedSubStream,
+ "SubStream1",
+ "MediaType1",
+ pBytes1,
+ aRelations1 ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStream( xClonedSubStream, "SubStream1" ) )
+ return false;
+
+
+ // commit substorage and check cloning
+
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ xClonedSubStorage = m_aTestHelper.cloneSubStorage( m_xMSF, m_xStorageFactory, xTempStorage, "SubStorage1" );
+ if ( xClonedSubStorage == null )
+ {
+ m_aTestHelper.Error( "The result of clone is empty!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xClonedSubStorage,
+ true,
+ ElementModes.WRITE,
+ aRelations2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xClonedSubStorage,
+ "SubStream2",
+ "MediaType2",
+ pBytes2,
+ aRelations2 ) )
+ return false;
+
+ XStorage xCloneOfRoot = m_aTestHelper.cloneStorage( m_xMSF, m_xStorageFactory, xTempStorage );
+ if ( xCloneOfRoot == null )
+ {
+ m_aTestHelper.Error( "The result of root clone is empty!" );
+ return false;
+ }
+
+ XNameAccess xCloneOfRootNA = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xCloneOfRoot );
+ if ( xCloneOfRootNA == null )
+ {
+ m_aTestHelper.Error( "XNameAccess is not implemented by the root clone!" );
+ return false;
+ }
+
+ if ( xCloneOfRootNA.hasElements() )
+ {
+ m_aTestHelper.Error( "The root storage still was not committed so it's clone must be empty!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.disposeStorage( xCloneOfRoot ) )
+ return false;
+
+ xCloneOfRoot = null;
+
+
+ // commit root storage and check cloning
+
+
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ xCloneOfRoot = m_aTestHelper.cloneStorage( m_xMSF, m_xStorageFactory, xTempStorage );
+ if ( xCloneOfRoot == null )
+ {
+ m_aTestHelper.Error( "The result of root clone is empty!" );
+ return false;
+ }
+
+ XStorage xSubStorageOfClone = xCloneOfRoot.openStorageElement( "SubStorage1", ElementModes.READ );
+ if ( xSubStorageOfClone == null )
+ {
+ m_aTestHelper.Error( "The result of root clone is wrong!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xSubStorageOfClone,
+ false,
+ ElementModes.READ,
+ aRelations2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xSubStorageOfClone,
+ "SubStream2",
+ "MediaType2",
+ pBytes2,
+ aRelations2 ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/ofopxmlstorages/Test08.java b/package/qa/ofopxmlstorages/Test08.java
new file mode 100644
index 0000000000..a2607141f9
--- /dev/null
+++ b/package/qa/ofopxmlstorages/Test08.java
@@ -0,0 +1,279 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.ofopxmlstorages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+import com.sun.star.beans.StringPair;
+
+import share.LogWriter;
+import complex.ofopxmlstorages.TestHelper;
+import complex.ofopxmlstorages.StorageTest;
+
+public class Test08 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test08( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test08: " );
+ }
+
+ public boolean test()
+ {
+ StringPair[][] aRelations1 =
+ { { new StringPair( "Id", "Num1" ) },
+ { new StringPair( "Target", "TargetURLValue1" ), new StringPair( "Id", "Num6" ) },
+ { new StringPair( "Target", "" ), new StringPair( "Id", "Num7" ) },
+ { new StringPair( "Id", "Num2" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num3" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num4" ), new StringPair( "TargetMode", "Internal1" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value 1" ) },
+ { new StringPair( "Id", "Num5" ), new StringPair( "TargetMode", "" ), new StringPair( "Type", "unknown1" ), new StringPair( "Target", "URL value1" ) }
+ };
+
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ XStorage xTempStorage = m_aTestHelper.createStorageFromStream( m_xStorageFactory,
+ xTempFileStream,
+ ElementModes.WRITE );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage,
+ "SubStream1",
+ "MediaType1",
+ true,
+ pBytes1,
+ aRelations1 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ true,
+ ElementModes.WRITE,
+ aRelations1 ) )
+ return false;
+
+ // set Relations for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ false,
+ ElementModes.WRITE,
+ aRelations1 ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+
+ // check substorage
+
+
+ if ( !checkSubStorages( xTempStorage, pBytes1, aRelations1 ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // now check all the written information with readwrite access
+
+
+ XStorage xResWriteStorage = m_aTestHelper.createStorageFromStream( m_xStorageFactory,
+ xTempFileStream,
+ ElementModes.WRITE );
+ if ( xResWriteStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open storage based on input stream!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResWriteStorage,
+ true,
+ ElementModes.WRITE,
+ aRelations1 ) )
+ return false;
+
+ if( !checkSubStorages( xResWriteStorage, pBytes1, aRelations1 ) )
+ return false;
+
+ // try to open for writing after opening for reading
+ XStorage xResWSubStorage = m_aTestHelper.openSubStorage( xResWriteStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xResWSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open substorage for writing after it was opened for reading!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResWSubStorage,
+ false,
+ ElementModes.WRITE,
+ aRelations1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResWSubStorage,
+ "SubStream1",
+ "MediaType1",
+ pBytes1,
+ aRelations1 ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xResWriteStorage ) )
+ return false;
+
+
+
+ // now check all the written information with readonly access
+
+
+ // close the output part of the temporary stream
+ // the output part must present since we already wrote to the stream
+ if ( !m_aTestHelper.closeOutput( xTempFileStream ) )
+ return false;
+
+ XInputStream xTempInStream = m_aTestHelper.getInputStream( xTempFileStream );
+ if ( xTempInStream == null )
+ return false;
+
+ // open input stream
+ // since no mode is provided the result storage must be opened readonly
+ XStorage xResultStorage = m_aTestHelper.createStorageFromInputStream( m_xStorageFactory,
+ xTempInStream );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open storage based on input stream!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage,
+ true,
+ ElementModes.READ,
+ aRelations1 ) )
+ return false;
+
+ if( !checkSubStorages( xResultStorage, pBytes1, aRelations1 ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+ private boolean checkSubStorages( XStorage xStorage, byte[] pBytes1, StringPair[][] aRelations )
+ {
+ XStorage xReadSubStorage1 = m_aTestHelper.openSubStorage( xStorage,
+ "SubStorage1",
+ ElementModes.READ );
+
+ XStorage xReadSubStorage2 = m_aTestHelper.openSubStorage( xStorage,
+ "SubStorage1",
+ ElementModes.READ );
+
+ if ( xReadSubStorage1 == null || xReadSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't open substorage for reading!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xReadSubStorage1,
+ false,
+ ElementModes.READ,
+ aRelations ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xReadSubStorage2,
+ false,
+ ElementModes.READ,
+ aRelations ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xReadSubStorage1,
+ "SubStream1",
+ "MediaType1",
+ pBytes1,
+ aRelations ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xReadSubStorage2,
+ "SubStream1",
+ "MediaType1",
+ pBytes1,
+ aRelations ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStorage( xReadSubStorage1 ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStorage( xReadSubStorage2 ) )
+ return false;
+
+ return true;
+ }
+}
+
diff --git a/package/qa/ofopxmlstorages/TestHelper.java b/package/qa/ofopxmlstorages/TestHelper.java
new file mode 100644
index 0000000000..b7f42ea7fd
--- /dev/null
+++ b/package/qa/ofopxmlstorages/TestHelper.java
@@ -0,0 +1,1111 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.ofopxmlstorages;
+
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.uno.AnyConverter;
+
+import com.sun.star.lang.*;
+import com.sun.star.embed.*;
+import com.sun.star.packages.*;
+import com.sun.star.io.*;
+import com.sun.star.beans.*;
+
+import share.LogWriter;
+
+public class TestHelper {
+
+ LogWriter m_aLogWriter;
+ String m_sTestPrefix;
+
+ public TestHelper( LogWriter aLogWriter, String sTestPrefix )
+ {
+ m_aLogWriter = aLogWriter;
+ m_sTestPrefix = sTestPrefix;
+ }
+
+ public boolean WriteBytesToStream( XStream xStream,
+ String sStreamName,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes,
+ StringPair[][] aRelations )
+ {
+ // get output stream of substream
+ XOutputStream xOutput = xStream.getOutputStream();
+ if ( xOutput == null )
+ {
+ Error( "Can't get XOutputStream implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // get XTruncate implementation from output stream
+ XTruncate xTruncate = (XTruncate) UnoRuntime.queryInterface( XTruncate.class, xOutput );
+ if ( xTruncate == null )
+ {
+ Error( "Can't get XTruncate implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // write requested byte sequence
+ try
+ {
+ xTruncate.truncate();
+ xOutput.writeBytes( pBytes );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't write to stream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStream );
+ if ( xPropSet == null )
+ {
+ Error( "Can't get XPropertySet implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // set properties to the stream
+ try
+ {
+ xPropSet.setPropertyValue( "MediaType", sMediaType );
+ xPropSet.setPropertyValue( "Compressed", Boolean.valueOf( bCompressed ) );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't set properties to substream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ // check size property of the stream
+ try
+ {
+ long nSize = AnyConverter.toLong( xPropSet.getPropertyValue( "Size" ) );
+ if ( nSize != pBytes.length )
+ {
+ Error( "The 'Size' property of substream '" + sStreamName + "' contains wrong value!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get 'Size' property from substream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ // get access to the relationship information
+ XRelationshipAccess xRelAccess = (XRelationshipAccess) UnoRuntime.queryInterface( XRelationshipAccess.class, xStream );
+ if ( xRelAccess == null )
+ {
+ Error( "Can't get XRelationshipAccess implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // set the relationship information
+ try
+ {
+ xRelAccess.insertRelationships( aRelations, false );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't set relationships to substream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xStream, sStreamName ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean WriteBytesToSubstream( XStorage xStorage,
+ String sStreamName,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes,
+ StringPair[][] aRelations )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xStorage.openStreamElement( sStreamName, ElementModes.WRITE );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't create substream '" + sStreamName + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
+ return false;
+ }
+
+ return WriteBytesToStream( xSubStream, sStreamName, sMediaType, bCompressed, pBytes, aRelations );
+ }
+
+ public boolean setStorageTypeAndCheckProps( XStorage xStorage,
+ boolean bIsRoot,
+ int nMode,
+ StringPair[][] aRelations )
+ {
+ boolean bOk = false;
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStorage );
+ if ( xPropSet != null )
+ {
+ try
+ {
+ // get "IsRoot" and "OpenMode" properties and control there values
+ boolean bPropIsRoot = AnyConverter.toBoolean( xPropSet.getPropertyValue( "IsRoot" ) );
+ int nPropMode = AnyConverter.toInt( xPropSet.getPropertyValue( "OpenMode" ) );
+
+ bOk = true;
+ if ( bPropIsRoot != bIsRoot )
+ {
+ Error( "'IsRoot' property contains wrong value!" );
+ bOk = false;
+ }
+
+ if ( ( bIsRoot
+ && ( nPropMode | ElementModes.READ ) != ( nMode | ElementModes.READ ) )
+ || ( !bIsRoot && ( nPropMode & nMode ) != nMode ) )
+ {
+ Error( "'OpenMode' property contains wrong value, expected " + nMode + ", in reality " + nPropMode + "!" );
+ bOk = false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't control properties of substorage, exception: " + e );
+ }
+ }
+ else
+ {
+ Error( "Can't get XPropertySet implementation from storage!" );
+ }
+
+ // get access to the relationship information
+ XRelationshipAccess xRelAccess = (XRelationshipAccess) UnoRuntime.queryInterface( XRelationshipAccess.class, xStorage );
+
+ if ( xRelAccess == null )
+ {
+ Error( "Can't get XRelationshipAccess implementation from the storage!" );
+ return false;
+ }
+
+ // set the relationship information
+ try
+ {
+ xRelAccess.insertRelationships( aRelations, false );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't set relationships to the storage, exception: " + e );
+ return false;
+ }
+
+
+ return bOk;
+ }
+
+ public boolean checkRelations( StringPair[][] aStorRels, StringPair[][] aTestRels )
+ {
+ if ( aStorRels.length != aTestRels.length )
+ {
+ Error( "The provided relations sequence has different size than the storage's one!" );
+ return false;
+ }
+
+ for ( int nStorInd = 0; nStorInd < aStorRels.length; nStorInd++ )
+ {
+ int nStorIDInd = -1;
+ for ( int nStorTagInd = 0; nStorTagInd < aStorRels[nStorInd].length; nStorTagInd++ )
+ {
+ if ( aStorRels[nStorInd][nStorTagInd].First.equals( "Id" ) )
+ {
+ nStorIDInd = nStorTagInd;
+ break;
+ }
+ }
+
+ if ( nStorIDInd == -1 )
+ {
+ Error( "One of the storage relations entries has no ID!" );
+ return false;
+ }
+
+ for ( int nInd = 0; nInd < aTestRels.length; nInd++ )
+ {
+ int nIDInd = -1;
+ for ( int nTagInd = 0; nTagInd < aTestRels[nInd].length; nTagInd++ )
+ {
+ if ( aTestRels[nInd][nTagInd].First.equals( "Id" ) )
+ {
+ nIDInd = nTagInd;
+ break;
+ }
+ }
+
+ if ( nIDInd == -1 )
+ {
+ Error( "One of the test hardcoded entries has no ID, num = " + nInd + ", length = " + aTestRels[nInd].length + ", global length = " + aTestRels.length + "!" );
+ return false;
+ }
+
+ if ( aStorRels[nStorInd][nStorIDInd].Second.equals( aTestRels[nInd][nIDInd].Second ) )
+ {
+ boolean[] pRelCheckMark = new boolean[ aTestRels[nInd].length ];
+ for ( int nCheckInd = 0; nCheckInd < pRelCheckMark.length; nCheckInd++ )
+ {
+ pRelCheckMark[nCheckInd] = false;
+ }
+
+ for ( int nStorTagInd = 0; nStorTagInd < aStorRels[nStorInd].length; nStorTagInd++ )
+ {
+ boolean bFound = false;
+ for ( int nTagInd = 0; nTagInd < aTestRels[nInd].length; nTagInd++ )
+ {
+ if ( aTestRels[nInd][nTagInd].First.equals( aStorRels[nStorInd][nStorTagInd].First ) )
+ {
+ if ( !aTestRels[nInd][nTagInd].Second.equals( aStorRels[nStorInd][nStorTagInd].Second ) )
+ {
+ Error( "Test rel. num. " + nInd + " has different tag \"" + aTestRels[nInd][nTagInd].First + "\" value!" );
+ return false;
+ }
+
+ bFound = true;
+ pRelCheckMark[nTagInd] = true;
+ break;
+ }
+ }
+
+ if ( !bFound )
+ {
+ Error( "Stor rel. num. " + nStorInd + " has unexpected tag \"" + aStorRels[nStorInd][nStorTagInd].First + "\", ID = \"" + aStorRels[nStorInd][nStorIDInd].Second + "\"!" );
+ return false;
+ }
+ }
+
+ for ( int nCheckInd = 0; nCheckInd < pRelCheckMark.length; nCheckInd++ )
+ {
+ if ( !pRelCheckMark[nCheckInd] && !aTestRels[nInd][nCheckInd].Second.equals( "" ) )
+ {
+ Error( "Test rel. num. " + nInd + " has unexpected tag \"" + aTestRels[nInd][nCheckInd].First + "\" with nonempty value!" );
+ return false;
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public boolean checkStorageProperties( XStorage xStorage,
+ boolean bIsRoot,
+ int nMode,
+ StringPair[][] aRelations )
+ {
+ boolean bOk = false;
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStorage );
+ if ( xPropSet != null )
+ {
+ try
+ {
+ // get "IsRoot" and "OpenMode" properties and control there values
+ boolean bPropIsRoot = AnyConverter.toBoolean( xPropSet.getPropertyValue( "IsRoot" ) );
+ int nPropMode = AnyConverter.toInt( xPropSet.getPropertyValue( "OpenMode" ) );
+
+ bOk = true;
+ if ( bPropIsRoot != bIsRoot )
+ {
+ Error( "'IsRoot' property contains wrong value!" );
+ bOk = false;
+ }
+
+ if ( ( bIsRoot
+ && ( nPropMode | ElementModes.READ ) != ( nMode | ElementModes.READ ) )
+ || ( !bIsRoot && ( nPropMode & nMode ) != nMode ) )
+ {
+ Error( "'OpenMode' property contains wrong value, expected " + nMode + ", in reality " + nPropMode + "!" );
+ bOk = false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get properties of substorage, exception: " + e );
+ }
+ }
+ else
+ {
+ Error( "Can't get XPropertySet implementation from storage!" );
+ }
+
+ // get access to the relationship information
+ XRelationshipAccess xRelAccess = (XRelationshipAccess) UnoRuntime.queryInterface( XRelationshipAccess.class, xStorage );
+
+ if ( xRelAccess == null )
+ {
+ Error( "Can't get XRelationshipAccess implementation from the checked storage!" );
+ return false;
+ }
+
+ // get the relationship information
+ StringPair[][] aStorRels;
+ try
+ {
+ aStorRels = xRelAccess.getAllRelationships();
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get relationships of the checked storage, exception: " + e );
+ return false;
+ }
+
+ if ( !checkRelations( aStorRels, aRelations ) )
+ {
+ Error( "StorageRelationsChecking has failed!" );
+ return false;
+ }
+
+ return bOk;
+ }
+
+ public boolean InternalCheckStream( XStream xStream,
+ String sName,
+ String sMediaType,
+ byte[] pBytes,
+ StringPair[][] aRelations )
+ {
+ // get input stream of substream
+ XInputStream xInput = xStream.getInputStream();
+ if ( xInput == null )
+ {
+ Error( "Can't get XInputStream implementation from substream '" + sName + "'!" );
+ return false;
+ }
+
+ byte pContents[][] = new byte[1][]; // ???
+
+ // read contents
+ try
+ {
+ xInput.readBytes( pContents, pBytes.length + 1 );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't read from stream '" + sName + "', exception: " + e );
+ return false;
+ }
+
+ // check size of stream data
+ if ( pContents.length == 0 )
+ {
+ Error( "SubStream '" + sName + "' reading produced disaster!" );
+ return false;
+ }
+
+ if ( pBytes.length != pContents[0].length )
+ {
+ Error( "SubStream '" + sName + "' contains wrong amount of data! (" + pContents[0].length + "/" + pBytes.length + ")" );
+ return false;
+ }
+
+ // check stream data
+ for ( int ind = 0; ind < pBytes.length; ind++ )
+ {
+ if ( pBytes[ind] != pContents[0][ind] )
+ {
+ Error( "SubStream '" + sName + "' contains wrong data! ( byte num. "
+ + ind + " should be" + pBytes[ind] + " but it is " + pContents[0][ind] + ")" );
+ return false;
+ }
+ }
+
+ // check properties
+ boolean bOk = false;
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStream );
+ if ( xPropSet != null )
+ {
+ try
+ {
+ // get "MediaType" and "Size" properties and control there values
+ String sPropMediaType = AnyConverter.toString( xPropSet.getPropertyValue( "MediaType" ) );
+ long nPropSize = AnyConverter.toLong( xPropSet.getPropertyValue( "Size" ) );
+
+ bOk = true;
+ if ( !sPropMediaType.equals( sMediaType ) )
+ {
+ Error( "'MediaType' property contains wrong value for stream '" + sName + "',\nexpected: '"
+ + sMediaType + "', set: '" + sPropMediaType + "'!" );
+ bOk = false;
+ }
+
+ if ( nPropSize != pBytes.length )
+ {
+ Error( "'Size' property contains wrong value for stream'" + sName + "'!" );
+ bOk = false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get properties of substream '" + sName + "', exception: " + e );
+ }
+ }
+ else
+ {
+ Error( "Can't get XPropertySet implementation from stream '" + sName + "'!" );
+ }
+
+
+ // get access to the relationship information
+ XRelationshipAccess xRelAccess = (XRelationshipAccess) UnoRuntime.queryInterface( XRelationshipAccess.class, xStream );
+
+ if ( xRelAccess == null )
+ {
+ Error( "Can't get XRelationshipAccess implementation from the stream\"" + sName + "\"!" );
+ return false;
+ }
+
+ // get the relationship information
+ StringPair[][] aStorRels;
+ try
+ {
+ aStorRels = xRelAccess.getAllRelationships();
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get relationships of the substream '" + sName + "', exception: " + e );
+ return false;
+ }
+
+ if ( !checkRelations( aStorRels, aRelations ) )
+ {
+ Error( "Stream '" + sName + "' RelationsChecking has failed!" );
+ return false;
+ }
+
+ return bOk;
+ }
+
+ public boolean checkStream( XStorage xParentStorage,
+ String sName,
+ String sMediaType,
+ byte[] pBytes,
+ StringPair[][] aRelations )
+ {
+ // open substream element first
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xParentStorage.openStreamElement( sName, ElementModes.READ );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't open substream '" + sName + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open substream '" + sName + "', exception : " + e + "!" );
+ return false;
+ }
+
+ boolean bResult = InternalCheckStream( xSubStream, sName, sMediaType, pBytes, aRelations );
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sName ) )
+ return false;
+
+ return bResult;
+ }
+
+ public boolean copyStorage( XStorage xSourceStorage, XStorage xDestStorage )
+ {
+ // copy xSourceStorage to xDestStorage
+ try
+ {
+ xSourceStorage.copyToStorage( xDestStorage );
+ }
+ catch( Exception e )
+ {
+ Error( "Storage copying failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean commitStorage( XStorage xStorage )
+ {
+ // XTransactedObject must be supported by storages
+ XTransactedObject xTransact = (XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xStorage );
+ if ( xTransact == null )
+ {
+ Error( "Storage doesn't implement transacted access!" );
+ return false;
+ }
+
+ try
+ {
+ xTransact.commit();
+ }
+ catch( Exception e )
+ {
+ Error( "Storage commit failed, exception:" + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean disposeStream( XStream xStream, String sStreamName )
+ {
+ XComponent xComponent = (XComponent) UnoRuntime.queryInterface( XComponent.class, xStream );
+ if ( xComponent == null )
+ {
+ Error( "Can't get XComponent implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ try
+ {
+ xComponent.dispose();
+ }
+ catch( Exception e )
+ {
+ Error( "Substream '" + sStreamName + "' disposing throws exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean disposeStorage( XStorage xStorage )
+ {
+ // dispose the storage
+ XComponent xComponent = (XComponent) UnoRuntime.queryInterface( XComponent.class, xStorage );
+ if ( xComponent == null )
+ {
+ Error( "Can't retrieve XComponent implementation from storage!" );
+ return false;
+ }
+
+ try
+ {
+ xComponent.dispose();
+ }
+ catch( Exception e )
+ {
+ Error( "Storage disposing failed!" );
+ return false;
+ }
+
+ return true;
+ }
+
+ public XInputStream getInputStream( XStream xStream )
+ {
+ XInputStream xInTemp = null;
+ try
+ {
+ xInTemp = xStream.getInputStream();
+ if ( xInTemp == null )
+ Error( "Can't get the input part of a stream!" );
+ }
+ catch ( Exception e )
+ {
+ Error( "Can't get the input part of a stream, exception :" + e );
+ }
+
+ return xInTemp;
+ }
+
+ public boolean closeOutput( XStream xStream )
+ {
+ XOutputStream xOutTemp = null;
+ try
+ {
+ xOutTemp = xStream.getOutputStream();
+ if ( xOutTemp == null )
+ {
+ Error( "Can't get the output part of a stream!" );
+ return false;
+ }
+ }
+ catch ( Exception e )
+ {
+ Error( "Can't get the output part of a stream, exception :" + e );
+ return false;
+ }
+
+ try
+ {
+ xOutTemp.closeOutput();
+ }
+ catch ( Exception e )
+ {
+ Error( "Can't close output part of a stream, exception :" + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public XStorage openSubStorage( XStorage xStorage, String sName, int nMode )
+ {
+ // open existing substorage
+ try
+ {
+ Object oSubStorage = xStorage.openStorageElement( sName, nMode );
+ XStorage xSubStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oSubStorage );
+ return xSubStorage;
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open substorage '" + sName + "', exception: " + e );
+ }
+
+ return null;
+ }
+
+ public XStream CreateTempFileStream( XMultiServiceFactory xMSF )
+ {
+ // try to get temporary file representation
+ XStream xTempFileStream = null;
+ try
+ {
+ Object oTempFile = xMSF.createInstance( "com.sun.star.io.TempFile" );
+ xTempFileStream = (XStream)UnoRuntime.queryInterface( XStream.class, oTempFile );
+ }
+ catch( Exception e )
+ {}
+
+ if ( xTempFileStream == null )
+ Error( "Can't create temporary file!" );
+
+ return xTempFileStream;
+ }
+
+ public String CreateTempFile( XMultiServiceFactory xMSF )
+ {
+ String sResult = null;
+
+ // try to get temporary file representation
+ XPropertySet xTempFileProps = null;
+ try
+ {
+ Object oTempFile = xMSF.createInstance( "com.sun.star.io.TempFile" );
+ xTempFileProps = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, oTempFile );
+ }
+ catch( Exception e )
+ {}
+
+ if ( xTempFileProps != null )
+ {
+ try
+ {
+ xTempFileProps.setPropertyValue( "RemoveFile", Boolean.FALSE );
+ sResult = AnyConverter.toString( xTempFileProps.getPropertyValue( "Uri" ) );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't control TempFile properties, exception: " + e );
+ }
+ }
+ else
+ {
+ Error( "Can't create temporary file representation!" );
+ }
+
+ // close temporary file explicitly
+ try
+ {
+ XStream xStream = (XStream)UnoRuntime.queryInterface( XStream.class, xTempFileProps );
+ if ( xStream != null )
+ {
+ XOutputStream xOut = xStream.getOutputStream();
+ if ( xOut != null )
+ xOut.closeOutput();
+
+ XInputStream xIn = xStream.getInputStream();
+ if ( xIn != null )
+ xIn.closeInput();
+ }
+ else
+ Error( "Can't close TempFile!" );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't close TempFile, exception: " + e );
+ }
+
+ return sResult;
+ }
+
+ public boolean copyElementTo( XStorage xSource, String sName, XStorage xDest )
+ {
+ // copy element with name sName from xSource to xDest
+ try
+ {
+ xSource.copyElementTo( sName, xDest, sName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element copying failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean copyElementTo( XStorage xSource, String sName, XStorage xDest, String sTargetName )
+ {
+ // copy element with name sName from xSource to xDest
+ try
+ {
+ xSource.copyElementTo( sName, xDest, sTargetName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element copying failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean moveElementTo( XStorage xSource, String sName, XStorage xDest )
+ {
+ // move element with name sName from xSource to xDest
+ try
+ {
+ xSource.moveElementTo( sName, xDest, sName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element moving failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean renameElement( XStorage xStorage, String sOldName, String sNewName )
+ {
+ // rename element with name sOldName to sNewName
+ try
+ {
+ xStorage.renameElement( sOldName, sNewName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element renaming failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean removeElement( XStorage xStorage, String sName )
+ {
+ // remove element with name sName
+ try
+ {
+ xStorage.removeElement( sName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element removing failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public XStream OpenStream( XStorage xStorage,
+ String sStreamName,
+ int nMode )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xStorage.openStreamElement( sStreamName, nMode );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ Error( "Can't create substream '" + sStreamName + "'!" );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
+ }
+
+ return xSubStream;
+ }
+
+ public boolean cantOpenStorage( XStorage xStorage, String sName )
+ {
+ // try to open an opened substorage, open call must fail
+ try
+ {
+ Object oDummyStorage = xStorage.openStorageElement( sName, ElementModes.READ );
+ Error( "The trying to reopen opened substorage '" + sName + "' must fail!" );
+ }
+ catch( Exception e )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean cantOpenStream( XStorage xStorage, String sName, int nMode )
+ {
+ // try to open the substream with specified mode must fail
+ try
+ {
+ Object oDummyStream = xStorage.openStreamElement( sName, nMode );
+ Error( "The trying to open substream '" + sName + "' must fail!" );
+ }
+ catch( Exception e )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public XStorage createStorageFromURL(
+ XSingleServiceFactory xFactory,
+ String aURL,
+ int nMode )
+ {
+ XStorage xResult = null;
+
+ try
+ {
+ PropertyValue[] aAddArgs = new PropertyValue[1];
+ aAddArgs[0] = new PropertyValue();
+ aAddArgs[0].Name = "StorageFormat";
+ aAddArgs[0].Value = "OFOPXMLFormat";
+
+ Object pArgs[] = new Object[3];
+ pArgs[0] = (Object) aURL;
+ pArgs[1] = Integer.valueOf( nMode );
+ pArgs[2] = (Object) aAddArgs;
+
+ Object oTempStorage = xFactory.createInstanceWithArguments( pArgs );
+ xResult = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create storage from URL, exception: " + e );
+ return null;
+ }
+
+ if ( xResult == null )
+ Error( "Can't create storage from URL!" );
+
+ return xResult;
+ }
+
+ public XStorage createStorageFromStream(
+ XSingleServiceFactory xFactory,
+ XStream xStream,
+ int nMode )
+ {
+ XStorage xResult = null;
+
+ try
+ {
+ PropertyValue[] aAddArgs = new PropertyValue[1];
+ aAddArgs[0] = new PropertyValue();
+ aAddArgs[0].Name = "StorageFormat";
+ aAddArgs[0].Value = "OFOPXMLFormat";
+
+ Object pArgs[] = new Object[3];
+ pArgs[0] = (Object) xStream;
+ pArgs[1] = Integer.valueOf( nMode );
+ pArgs[2] = (Object) aAddArgs;
+
+ Object oTempStorage = xFactory.createInstanceWithArguments( pArgs );
+ xResult = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create storage from stream, exception: " + e );
+ return null;
+ }
+
+ if ( xResult == null )
+ Error( "Can't create storage from stream!" );
+
+ return xResult;
+ }
+
+ public XStorage createStorageFromInputStream(
+ XSingleServiceFactory xFactory,
+ XInputStream xInStream )
+ {
+ XStorage xResult = null;
+
+ try
+ {
+ PropertyValue[] aAddArgs = new PropertyValue[1];
+ aAddArgs[0] = new PropertyValue();
+ aAddArgs[0].Name = "StorageFormat";
+ aAddArgs[0].Value = "OFOPXMLFormat";
+
+ Object pArgs[] = new Object[3];
+ pArgs[0] = (Object) xInStream;
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ pArgs[2] = (Object) aAddArgs;
+
+ Object oTempStorage = xFactory.createInstanceWithArguments( pArgs );
+ xResult = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create storage from input stream, exception: " + e );
+ return null;
+ }
+
+ if ( xResult == null )
+ Error( "Can't create storage from input stream!" );
+
+ return xResult;
+ }
+
+ public XStorage createTempStorage( XMultiServiceFactory xMSF, XSingleServiceFactory xFactory )
+ {
+ // create a temporary storage
+ XStorage xResult = null;
+ XStream xStream = CreateTempFileStream( xMSF );
+ if ( xStream == null )
+ {
+ Error( "Can't create temp file stream!" );
+ return null;
+ }
+
+ try
+ {
+ xResult = createStorageFromStream( xFactory, xStream, ElementModes.WRITE );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create temp storage, exception: " + e );
+ }
+
+ return xResult;
+ }
+
+ public XStorage cloneStorage( XMultiServiceFactory xMSF, XSingleServiceFactory xFactory, XStorage xStorage )
+ {
+ // create a copy of a last committed version of specified storage
+ XStorage xResult = null;
+ try
+ {
+ xResult = createTempStorage( xMSF, xFactory );
+ if ( xResult != null )
+ xStorage.copyLastCommitTo( xResult );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't clone storage, exception: " + e );
+ return null;
+ }
+
+ return xResult;
+ }
+
+ public XStorage cloneSubStorage( XMultiServiceFactory xMSF, XSingleServiceFactory xFactory, XStorage xStorage, String sName )
+ {
+ // create a copy of a last committed version of specified substorage
+ XStorage xResult = null;
+ try
+ {
+ xResult = createTempStorage( xMSF, xFactory );
+ if ( xResult != null )
+ xStorage.copyStorageElementLastCommitTo( sName, xResult );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't clone substorage '" + sName + "', exception: " + e );
+ return null;
+ }
+
+ return xResult;
+ }
+
+ public XStream cloneSubStream( XStorage xStorage, String sName )
+ {
+ // clone existing substream
+ try
+ {
+ XStream xStream = xStorage.cloneStreamElement( sName );
+ return xStream;
+ }
+ catch( Exception e )
+ {
+ Error( "Can't clone substream '" + sName + "', exception: " + e );
+ }
+
+ return null;
+ }
+
+ public void Error( String sError )
+ {
+ m_aLogWriter.println( m_sTestPrefix + "Error: " + sError );
+ }
+
+ public void Message( String sMessage )
+ {
+ m_aLogWriter.println( m_sTestPrefix + sMessage );
+ }
+
+ public void PrintRelations( StringPair[][] aRels )
+ {
+ m_aLogWriter.println( "========" );
+ for ( int nInd1 = 0; nInd1 < aRels.length; nInd1++ )
+ {
+ for ( int nInd2 = 0; nInd2 < aRels[nInd1].length; nInd2++ )
+ {
+ m_aLogWriter.println( "\"" + aRels[nInd1][nInd2].First + "\" = \"" + aRels[nInd1][nInd2].Second + "\", " );
+ }
+ m_aLogWriter.println( "========" );
+ }
+ }
+}
+
diff --git a/package/qa/ofopxmlstorages/makefile.mk b/package/qa/ofopxmlstorages/makefile.mk
new file mode 100644
index 0000000000..5cf93e98e0
--- /dev/null
+++ b/package/qa/ofopxmlstorages/makefile.mk
@@ -0,0 +1,82 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+PRJ = ..$/..
+TARGET = StorageUnitTest
+PRJNAME = package
+PACKAGE = complex$/ofopxmlstorages
+
+# --- Settings -----------------------------------------------------
+.INCLUDE: settings.mk
+
+
+#----- compile .java files -----------------------------------------
+
+JARFILES = ridl.jar unoil.jar jurt.jar juh.jar java_uno.jar OOoRunner.jar
+
+JAVAFILES =\
+ StorageUnitTest.java\
+ StorageTest.java\
+ TestHelper.java\
+ Test01.java\
+ Test02.java\
+ Test03.java\
+ Test04.java\
+ Test05.java\
+ Test06.java\
+ Test07.java\
+ Test08.java
+
+JAVACLASSFILES = $(foreach,i,$(JAVAFILES) $(CLASSDIR)$/$(PACKAGE)$/$(i:b).class)
+
+#----- make a jar from compiled files ------------------------------
+
+MAXLINELENGTH = 100000
+
+JARCLASSDIRS = $(PACKAGE)
+JARTARGET = $(TARGET).jar
+JARCOMPRESS = TRUE
+
+# --- Parameters for the test --------------------------------------
+
+# start an office if the parameter is set for the makefile
+.IF "$(OFFICE)" == ""
+CT_APPEXECCOMMAND =
+.ELSE
+CT_APPEXECCOMMAND = -AppExecutionCommand "$(OFFICE)$/soffice --accept=socket,host=localhost,port=8100;urp;"
+.ENDIF
+
+# test base is java complex
+CT_TESTBASE = -TestBase java_complex
+
+# test looks something like the.full.package.TestName
+CT_TEST = -o $(PACKAGE:s\$/\.\).$(JAVAFILES:b)
+
+# start the runner application
+CT_APP = org.openoffice.Runner
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE: target.mk
+
+RUN: run
+
+run:
+ java -cp $(CLASSPATH) $(CT_APP) $(CT_TESTBASE) $(CT_APPEXECCOMMAND) $(CT_TEST)
+
+
diff --git a/package/qa/storages/BorderedStream.java b/package/qa/storages/BorderedStream.java
new file mode 100644
index 0000000000..ce7ebe55ff
--- /dev/null
+++ b/package/qa/storages/BorderedStream.java
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+import com.sun.star.io.XOutputStream;
+import com.sun.star.io.XTruncate;
+import com.sun.star.io.XSeekable;
+
+
+public class BorderedStream
+ implements XStream, XInputStream, XOutputStream, XTruncate, XSeekable
+{
+ int m_nMaxSize;
+ int m_nCurSize;
+ int m_nCurPos;
+ byte m_pBytes[];
+
+ public BorderedStream( int nMaxSize )
+ {
+ m_nMaxSize = nMaxSize;
+ m_nCurSize = 0;
+ m_nCurPos = 0;
+ m_pBytes = new byte[m_nMaxSize];
+ }
+
+
+ // XStream
+
+
+
+ public synchronized XInputStream getInputStream()
+ throws com.sun.star.uno.RuntimeException
+ {
+ return (XInputStream)UnoRuntime.queryInterface( XInputStream.class, this );
+ }
+
+
+ public synchronized XOutputStream getOutputStream()
+ throws com.sun.star.uno.RuntimeException
+ {
+ return (XOutputStream)UnoRuntime.queryInterface( XOutputStream.class, this );
+ }
+
+
+ // XInputStream
+
+
+
+ public synchronized int readBytes( byte[][] aData, int nBytesToRead )
+ throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ int nRead = 0;
+ if ( m_pBytes != null && nBytesToRead > 0 )
+ {
+ int nAvailable = m_nCurSize - m_nCurPos;
+ if ( nBytesToRead > nAvailable )
+ nBytesToRead = nAvailable;
+
+ aData[0] = new byte[nBytesToRead];
+ for ( int nInd = 0; nInd < nBytesToRead; nInd++ )
+ aData[0][nInd] = m_pBytes[m_nCurPos+nInd];
+
+ nRead = nBytesToRead;
+ m_nCurPos += nRead;
+ }
+ else
+ {
+ aData[0] = new byte[0];
+ }
+
+ return nRead;
+ }
+
+
+ public synchronized int readSomeBytes( byte[][] aData, int nMaxBytesToRead )
+ throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ return readBytes( aData, nMaxBytesToRead );
+ }
+
+
+ public synchronized void skipBytes( int nBytesToSkip )
+ throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ if ( nBytesToSkip < 0 )
+ throw new com.sun.star.io.IOException(); // illegal argument
+
+ if ( m_nCurSize - m_nCurPos > nBytesToSkip )
+ m_nCurPos += nBytesToSkip;
+ else
+ m_nCurPos = m_nCurSize;
+ }
+
+
+ public synchronized int available()
+ throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ return 0;
+ }
+
+
+ public synchronized void closeInput()
+ throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ // no need to do anything
+ }
+
+
+
+ // XOutputStream
+
+
+
+ public synchronized void writeBytes( byte[] aData )
+ throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ if ( m_pBytes != null && aData.length > 0 )
+ {
+ if ( aData.length > m_nMaxSize - m_nCurPos )
+ throw new com.sun.star.io.IOException();
+
+ for ( int nInd = 0; nInd < aData.length; nInd++ )
+ m_pBytes[m_nCurPos+nInd] = aData[nInd];
+
+ m_nCurPos += aData.length;
+ if ( m_nCurPos > m_nCurSize )
+ m_nCurSize = m_nCurPos;
+ }
+ }
+
+
+ public synchronized void flush()
+ throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ // nothing to do
+ }
+
+
+ public synchronized void closeOutput()
+ throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ // nothing to do
+ }
+
+
+
+ // XTruncate
+
+
+
+ public synchronized void truncate()
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ m_nCurSize = 0;
+ m_nCurPos = 0;
+ }
+
+
+
+ // XSeekable
+
+
+
+ public synchronized void seek( long location )
+ throws com.sun.star.lang.IllegalArgumentException, com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ if ( location > (long)m_nCurSize )
+ throw new com.sun.star.lang.IllegalArgumentException();
+
+ m_nCurPos = (int)location;
+ }
+
+
+ public synchronized long getPosition()
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ return (long)m_nCurPos;
+ }
+
+
+ public synchronized long getLength()
+ throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException
+ {
+ return (long)m_nCurSize;
+ }
+};
+
diff --git a/package/qa/storages/RegressionTest_114358.java b/package/qa/storages/RegressionTest_114358.java
new file mode 100644
index 0000000000..f76216cf22
--- /dev/null
+++ b/package/qa/storages/RegressionTest_114358.java
@@ -0,0 +1,208 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_114358 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_114358( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_114358: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType2",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // create a new storage based on the stream and change the substream
+ // as described in the bug description
+
+
+ byte pBytes2[] = { 2, 2 };
+
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open the substream, set new "MediaType" and "Compressed" properties to it, truncate and write new contents
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType4", true, pBytes2 ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // create a new readonly storage based on the stream and check the contents
+
+
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempStorage, "MediaType2", true, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ // the MediaType and the contents must be up to date
+ if ( !m_aTestHelper.checkStream( xTempSubStorage, "SubStream1", "MediaType4", true, pBytes2 ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_125919.java b/package/qa/storages/RegressionTest_125919.java
new file mode 100644
index 0000000000..3ad8b0e6ce
--- /dev/null
+++ b/package/qa/storages/RegressionTest_125919.java
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import java.lang.Integer;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+import complex.storages.BorderedStream;
+
+public class RegressionTest_125919 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ int nMinTestLen = 0;
+ int nMaxTestLen = 60000;
+
+ public RegressionTest_125919( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_125919: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ byte[] pBytes0 = new byte[0];
+ byte[] pBytes18 = new byte[18000];
+ byte[] pBytes36 = new byte[36000];
+
+ for ( int nInitInd = 0; nInitInd < 36000; nInitInd++ )
+ {
+ pBytes36[nInitInd] = ( Integer.valueOf( nInitInd >> ( ( nInitInd % 2 ) * 8 ) ) ).byteValue();
+ if ( nInitInd < 18000 )
+ pBytes18[nInitInd] = ( Integer.valueOf( 256 - pBytes36[nInitInd] ) ).byteValue();
+ }
+
+ System.out.println( "This test can take up to some hours. The file size currently is about 50000." );
+ System.out.println( "Progress: " );
+ for ( int nAvailableBytes = nMinTestLen; nAvailableBytes < nMaxTestLen; nAvailableBytes++ )
+ {
+ Object oBStream = new BorderedStream( nAvailableBytes );
+ XStream xBorderedStream = (XStream)UnoRuntime.queryInterface( XStream.class, oBStream );
+ if ( xBorderedStream == null )
+ {
+ m_aTestHelper.Error( "Can't create bordered stream!" );
+ return false;
+ }
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xBorderedStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ XTransactedObject xTransact = (XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xTempStorage );
+ if ( xTransact == null )
+ {
+ m_aTestHelper.Error( "This test is designed for storages in transacted mode!" );
+ return false;
+ }
+
+
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "SubStream" + 0, "MediaType1", true, pBytes0 ) )
+ return false;
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "SubStream" + 18, "MediaType2", true, pBytes18 ) )
+ return false;
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "SubStream" + 36, "MediaType3", true, pBytes36 ) )
+ return false;
+
+ if ( nAvailableBytes > 0 && nAvailableBytes % 100 == 0 )
+ System.out.println( " " + nAvailableBytes );
+
+ if ( nAvailableBytes > 0 && nAvailableBytes % 2 == 1 )
+ System.out.print( "#" );
+
+ try
+ {
+ xTransact.commit();
+
+ System.out.println( "" );
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+ // SUCCESS
+ return true;
+ }
+ catch( UseBackupException aExc )
+ {
+ // when there is not enough place in the target location and the target file is empty
+ // the direct writing will fail and must throw this exception with empty URL
+ if ( aExc.TemporaryFileURL.length() != 0 )
+ return false;
+ }
+ catch( Exception e )
+ {
+ System.out.println( "" );
+ m_aTestHelper.Error( "Unexpected exception: " + e + "\nnAvailableBytes = " + nAvailableBytes );
+ return false;
+ }
+ }
+
+ return false;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i26398.java b/package/qa/storages/RegressionTest_i26398.java
new file mode 100644
index 0000000000..ec14400c8f
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i26398.java
@@ -0,0 +1,164 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i26398 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i26398( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i26398: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType2",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+
+
+ // commit the substorage, dispose it, reopen readonly
+ // and dispose the reopened substorage
+
+
+ // commit substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // open a new substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+
+ // reopen the substorage in readwrite mode and check contents
+
+
+ // open a new substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage, "MediaType3", false, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempStorage, "MediaType2", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // the root storage is based on the temporary stream so it can be left undisposed, since it does not lock
+ // any resource, later the garbage collector will release the object and it must die by refcount
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i27773.java b/package/qa/storages/RegressionTest_i27773.java
new file mode 100644
index 0000000000..f5e455192a
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i27773.java
@@ -0,0 +1,317 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.uno.AnyConverter;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+
+// Tests also fix for i51352
+
+
+public class RegressionTest_i27773 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i27773( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i27773: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ if ( true )
+ {
+ // for debugging proposes
+
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xTempFileStream );
+ if ( xPropSet != null )
+ {
+ try
+ {
+ String sTempURL = AnyConverter.toString( xPropSet.getPropertyValue( "Uri" ) );
+ // m_aTestHelper.Message( "URL: " + sTempURL );
+ xPropSet.setPropertyValue( "RemoveFile", Boolean.FALSE );
+ }
+ catch ( Exception e )
+ {
+ }
+ }
+ }
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open an empty substorage
+ XStorage xEmptySubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "EmptySubStorage1",
+ ElementModes.WRITE );
+ if ( xEmptySubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open an empty substorage
+ XStorage xEmptySubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "EmptySubSubStorage1",
+ ElementModes.WRITE );
+ if ( xEmptySubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType2",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xEmptySubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xEmptySubSubStorage,
+ "MediaType5",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // make a copy of substorage
+
+ if ( !m_aTestHelper.copyElementTo( xTempStorage, "SubStorage1", xTempStorage, "SubStorage1_copy" ) )
+ return false;
+
+ if ( !m_aTestHelper.copyElementTo( xTempStorage, "EmptySubStorage1", xTempStorage, "EmptySubStorage1_copy" ) )
+ return false;
+
+
+ // copy all the changed and noncommitted substorages
+ // and dispose them
+
+
+ if ( !m_aTestHelper.commitStorage( xEmptySubSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xEmptySubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose substorages
+
+ if ( !m_aTestHelper.disposeStorage( xEmptySubSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStorage( xEmptySubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // reopen the storage in readonly mode and check contents
+
+
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open original substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open copy of the original substorage
+ XStorage xTempSubStorage_copy = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1_copy",
+ ElementModes.READ );
+ if ( xTempSubStorage_copy == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open empty substorage
+ xEmptySubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "EmptySubStorage1",
+ ElementModes.READ );
+ if ( xEmptySubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open copy of empty substorage
+ XStorage xEmptySubStorage_copy = m_aTestHelper.openSubStorage( xTempStorage,
+ "EmptySubStorage1_copy",
+ ElementModes.READ );
+ if ( xEmptySubStorage_copy == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open an empty substorage of the substorage
+ xEmptySubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "EmptySubSubStorage1",
+ ElementModes.READ );
+ if ( xEmptySubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open an empty substorage of the substorage copy
+ XStorage xEmptySubSubStorage_inCopy = m_aTestHelper.openSubStorage( xTempSubStorage_copy,
+ "EmptySubSubStorage1",
+ ElementModes.READ );
+ if ( xEmptySubSubStorage_inCopy == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+
+ // check contents
+
+ if ( !m_aTestHelper.checkStorageProperties( xEmptySubSubStorage, "MediaType5", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xEmptySubSubStorage_inCopy, "MediaType5", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage_copy, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xEmptySubStorage, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xEmptySubStorage_copy, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempStorage, "MediaType2", true, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubStorage_copy, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // the root storage is based on the temporary stream so it can be left undisposed, since it does not lock
+ // any resource, later the garbage collector will release the object and it must die by refcount
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i29169.java b/package/qa/storages/RegressionTest_i29169.java
new file mode 100644
index 0000000000..12286e7589
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i29169.java
@@ -0,0 +1,387 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i29169 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i29169( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i29169: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // open a new substorage in the existing substorage
+ XStorage xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubSubStorage, "SubSubStream1", "MediaType2", true, pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubSubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType5",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // commit the storages, and check the renaming in all stages
+
+
+ // rename the storage before it is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubSubStorage1", "SubSubStorage2" ) )
+ return false;
+
+ // rename the stream
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubStream1", "SubStream2" ) )
+ return false;
+
+ // commit lowlevel substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubSubStorage ) )
+ return false;
+
+ // rename the storage after it is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubSubStorage2", "SubSubStorage3" ) )
+ return false;
+
+ // rename the stream one more time
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubStream2", "SubStream3" ) )
+ return false;
+
+ // commit substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // rename the storage after it's parent is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubSubStorage3", "SubSubStorage4" ) )
+ return false;
+
+ // rename the stream after it's parent is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubStream3", "SubStream4" ) )
+ return false;
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // rename the storage after the package is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubSubStorage4", "SubSubStorage5" ) )
+ return false;
+
+ // rename the stream after it's parent is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubStream4", "SubStream5" ) )
+ return false;
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+
+ // dispose the storages
+
+
+ // dispose lowerest substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubSubStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // create a new storage based on the stream and check the substreams and substorages
+
+
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open the lowlevel substorage
+ xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage5",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // check the storages and streams
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubSubStorage, "MediaType3", false, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage, "MediaType4", false, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempStorage, "MediaType5", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubStorage, "SubStream5", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubSubStorage, "SubSubStream1", "MediaType2", true, pBytes1 ) )
+ return false;
+
+
+ // rename the reopened storages and streams
+
+
+ // rename the storage before it is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubSubStorage5", "SubSubStorage6" ) )
+ return false;
+
+ // rename the stream
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubStream5", "SubStream6" ) )
+ return false;
+
+ // commit lowlevel substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubSubStorage ) )
+ return false;
+
+ // rename the storage after it is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubSubStorage6", "SubSubStorage7" ) )
+ return false;
+
+ // rename the stream one more time
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubStream6", "SubStream7" ) )
+ return false;
+
+ // commit substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // rename the storage after it's parent is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubSubStorage7", "SubSubStorage8" ) )
+ return false;
+
+ // rename the stream after it's parent is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubStream7", "SubStream8" ) )
+ return false;
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // rename the storage after the package is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubSubStorage8", "SubSubStorage9" ) )
+ return false;
+
+ // rename the stream after it`s parent is committed
+ if ( !m_aTestHelper.renameElement( xTempSubStorage, "SubStream8", "SubStream9" ) )
+ return false;
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+
+ // dispose the storages
+
+
+ // dispose lowerest substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubSubStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+
+ // create a new readonly storage based on the stream and check the contents
+
+
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open the lowlevel substorage
+ xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage9",
+ ElementModes.READ );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // check the storages and streams
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubSubStorage, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempStorage, "MediaType5", true, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubStorage, "SubStream9", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubSubStorage, "SubSubStream1", "MediaType2", true, pBytes1 ) )
+ return false;
+
+ // the storage is based on the temporary stream so it can be left undisposed, since it does not lock
+ // any resource, later the garbage collector will release the object and it must die by refcount
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i29321.java b/package/qa/storages/RegressionTest_i29321.java
new file mode 100644
index 0000000000..8fe980ba9d
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i29321.java
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i29321 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i29321( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i29321: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "Stream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType2", true, pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubSubStorage, "SubSubStream1", "MediaType3", true, pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType4",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType5",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubSubStorage,
+ "MediaType6",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // commit the storages twice to test the bug scenario
+
+
+ // commit lowlevel substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubSubStorage ) )
+ return false;
+
+ // commit substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // commit lowlevel substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubSubStorage ) )
+ return false;
+
+ // commit substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+
+ // check the storages and streams without closing
+
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubSubStorage, "MediaType6", false, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage, "MediaType5", false, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempStorage, "MediaType4", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubSubStorage, "SubSubStream1", "MediaType3", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubStorage, "SubStream1", "MediaType2", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempStorage, "Stream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // the root storage is based on the temporary stream so it can be left undisposed, since it does not lock
+ // any resource, later the garbage collector will release the object and it must die by refcount
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i30400.java b/package/qa/storages/RegressionTest_i30400.java
new file mode 100644
index 0000000000..39866503b6
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i30400.java
@@ -0,0 +1,453 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i30400 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i30400( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i30400: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+
+ // create a temporary stream and a storage based on it
+ // fill the storage with the data that will be used for testing
+
+
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+ String pPass1 = "1, 2, 3, 4, 5";
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "Stream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "EncrStream1", "MediaType2", true, pBytes1, pPass1 ) )
+ return false;
+
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType3", true, pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempSubStorage, "SubEncrStream1", "MediaType4", true, pBytes1, pPass1 ) )
+ return false;
+
+ // open a new substorage in the existing substorage
+ XStorage xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubSubStorage, "SubSubStream1", "MediaType5", true, pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubSubStorage,
+ "MediaType6",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType7",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType8",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // check the copying with renaming
+
+
+ if ( !TestCopyWithRenaming( xTempStorage, xTempSubStorage, xTempSubSubStorage ) )
+ return false;
+
+
+ // commit the storages
+
+
+ // commit lowlevel substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubSubStorage ) )
+ return false;
+
+ // commit substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+
+ // dispose the storages
+
+
+ // dispose lowerest substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubSubStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // reopen the target storage readonly, and check the copying with renaming
+
+
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorages
+
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open the lowlevel substorages
+
+ xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage1",
+ ElementModes.READ );
+ if ( xTempSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // test the copying with renaming
+ if ( !TestCopyWithRenaming( xTempStorage, xTempSubStorage, xTempSubSubStorage ) )
+ return false;
+
+
+ // the storage is based on the temporary stream so it can be left undisposed, since it does not lock
+ // any resource, later the garbage collector will release the object and it must die by refcount
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+
+ public boolean TestCopyWithRenaming( XStorage xTempStorage, XStorage xTempSubStorage, XStorage xTempSubSubStorage )
+ throws com.sun.star.uno.Exception
+ {
+
+ // create a second temporary stream and copy all the staff there
+ // with renaming, check the success
+
+
+ XStream xTempFileStream2 = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream2 == null )
+ return false;
+
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream2;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+ String pPass1 = "1, 2, 3, 4, 5";
+
+ Object oTempStorage2 = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage2 = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage2 );
+ if ( xTempStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage2 = m_aTestHelper.openSubStorage( xTempStorage2,
+ "SubStorage1_target",
+ ElementModes.WRITE );
+ if ( xTempSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substorage in the existing substorage
+ XStorage xTempSubSubStorage2 = m_aTestHelper.openSubStorage( xTempSubStorage2,
+ "SubSubStorage1_target",
+ ElementModes.WRITE );
+ if ( xTempSubSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // make a copy with renaming on lowerest level
+ if ( !m_aTestHelper.copyElementTo( xTempSubSubStorage, "SubSubStream1", xTempSubSubStorage2, "SubSubStream1_renamed" ) )
+ return false;
+
+ // make a copy with renaming on the next level
+
+ if ( !m_aTestHelper.copyElementTo( xTempSubStorage, "SubStream1", xTempSubStorage2, "SubStream1_renamed" ) )
+ return false;
+
+ if ( !m_aTestHelper.copyElementTo( xTempSubStorage, "SubEncrStream1", xTempSubStorage2, "SubEncrStream1_renamed" ) )
+ return false;
+
+ if ( !m_aTestHelper.copyElementTo( xTempSubStorage, "SubSubStorage1", xTempSubStorage2, "SubSubStorage1_renamed" ) )
+ return false;
+
+ // make a copy with renaming of subelements of the root storage
+
+ if ( !m_aTestHelper.copyElementTo( xTempStorage, "Stream1", xTempStorage2, "Stream1_renamed" ) )
+ return false;
+
+ if ( !m_aTestHelper.copyElementTo( xTempStorage, "EncrStream1", xTempStorage2, "EncrStream1_renamed" ) )
+ return false;
+
+ if ( !m_aTestHelper.copyElementTo( xTempStorage, "SubStorage1", xTempStorage2, "SubStorage1_renamed" ) )
+ return false;
+
+
+ // commit the storages, and check the renaming in all stages
+
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempSubSubStorage2 ) )
+ return false;
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage2 ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage2 ) )
+ return false;
+
+
+ // dispose the storages
+
+
+ // dispose lowerest substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubSubStorage2 ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage2 ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage2 ) )
+ return false;
+
+
+ // reopen the target storage readonly, and check the contents
+
+
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ oTempStorage2 = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage2 = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage2 );
+ if ( xTempStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorages
+
+ XStorage xTempSubStorage2_target = m_aTestHelper.openSubStorage( xTempStorage2,
+ "SubStorage1_target",
+ ElementModes.READ );
+ if ( xTempSubStorage2_target == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ XStorage xTempSubStorage2_renamed = m_aTestHelper.openSubStorage( xTempStorage2,
+ "SubStorage1_renamed",
+ ElementModes.READ );
+ if ( xTempSubStorage2_renamed == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open the lowlevel substorages
+
+ XStorage xTempSubSubStorage2_inRenamed = m_aTestHelper.openSubStorage( xTempSubStorage2_renamed,
+ "SubSubStorage1",
+ ElementModes.READ );
+ if ( xTempSubSubStorage2_inRenamed == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ XStorage xTempSubSubStorage2_renamed = m_aTestHelper.openSubStorage( xTempSubStorage2_target,
+ "SubSubStorage1_renamed",
+ ElementModes.READ );
+ if ( xTempSubSubStorage2_renamed == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ XStorage xTempSubSubStorage2_target = m_aTestHelper.openSubStorage( xTempSubStorage2_target,
+ "SubSubStorage1_target",
+ ElementModes.READ );
+ if ( xTempSubSubStorage2_target == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // check the storages
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubSubStorage2_inRenamed, "MediaType6", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubSubStorage2_renamed, "MediaType6", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage2_renamed, "MediaType7", false, ElementModes.READ ) )
+ return false;
+
+
+ // check the streams
+
+
+ // sub sub level
+
+ if ( !m_aTestHelper.checkStream( xTempSubSubStorage2_inRenamed, "SubSubStream1", "MediaType5", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubSubStorage2_renamed, "SubSubStream1", "MediaType5", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubSubStorage2_target, "SubSubStream1_renamed", "MediaType5", true, pBytes1 ) )
+ return false;
+
+ // sub level
+
+ if ( !m_aTestHelper.checkStream( xTempSubStorage2_renamed, "SubStream1", "MediaType3", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xTempSubStorage2_renamed, "SubEncrStream1", "MediaType4", pBytes1, pPass1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubStorage2_target, "SubStream1_renamed", "MediaType3", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xTempSubStorage2_target, "SubEncrStream1_renamed", "MediaType4", pBytes1, pPass1 ) )
+ return false;
+
+ // root storage level
+
+ if ( !m_aTestHelper.checkStream( xTempStorage2, "Stream1_renamed", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xTempStorage2, "EncrStream1_renamed", "MediaType2", pBytes1, pPass1 ) )
+ return false;
+
+ // the storage is based on the temporary stream so it can be left undisposed, since it does not lock
+ // any resource, later the garbage collector will release the object and it must die by refcount
+
+ return true;
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i30677.java b/package/qa/storages/RegressionTest_i30677.java
new file mode 100644
index 0000000000..faee91afe5
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i30677.java
@@ -0,0 +1,281 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i30677 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i30677( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i30677: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new subsubstorage
+ XStorage xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubSubStorage, "SubSubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType2",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // commit the storages
+
+
+ // commit lowlevel substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubSubStorage ) )
+ return false;
+
+ // commit substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+
+ // dispose the storages
+
+
+ // dispose lowerest substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubSubStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // reopen the storage and rewrite the stream
+
+
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorages
+
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open the lowlevel substorages
+
+ xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubSubStorage, "SubSubStream1", "MediaType1", true, pBytes2 ) )
+ return false;
+
+
+ // commit the storages
+
+
+ // commit lowlevel substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubSubStorage ) )
+ return false;
+
+ // commit substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit substorage to let the renaming take place
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+
+ // dispose the storages
+
+
+ // dispose lowerest substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubSubStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // reopen the storages and check the contents
+
+
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorages
+
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open the lowlevel substorages
+
+ xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage1",
+ ElementModes.READ );
+ if ( xTempSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubSubStorage, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempStorage, "MediaType2", true, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubSubStorage, "SubSubStream1", "MediaType1", true, pBytes2 ) )
+ return false;
+
+ // the root storage is based on the temporary stream so it can be left undisposed, since it does not lock
+ // any resource, later the garbage collector will release the object and it must die by refcount
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i35095.java b/package/qa/storages/RegressionTest_i35095.java
new file mode 100644
index 0000000000..0c979466c3
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i35095.java
@@ -0,0 +1,184 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i35095 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i35095( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i35095: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ byte pBytes[] = new byte[36000];
+ for ( int nInd = 0; nInd < 36000; nInd++ )
+ pBytes[nInd] = (byte)( nInd % 128 );
+
+ String sPass = "12345";
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "SubStream1", "MediaType1", true, pBytes, sPass ) )
+ return false;
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempSubStorage, "SubStream2", "MediaType2", false, pBytes, sPass ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // now check all the written information
+ // and the raw stream contents
+
+
+ // close the output part of the temporary stream
+ // the output part must present since we already wrote to the stream
+ if ( !m_aTestHelper.closeOutput( xTempFileStream ) )
+ return false;
+
+ XInputStream xTempInStream = m_aTestHelper.getInputStream( xTempFileStream );
+ if ( xTempInStream == null )
+ return false;
+
+ // open input stream
+ // since no mode is provided the result storage must be opened readonly
+ Object pOneArg[] = new Object[1];
+ pOneArg[0] = (Object) xTempInStream;
+
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pOneArg );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open storage based on input stream!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType3", true, ElementModes.READ ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.compareRawMethodsOnEncrStream( xResultStorage, "SubStream1" ) )
+ return false;
+
+ if ( !m_aTestHelper.compareRawMethodsOnEncrStream( xResultSubStorage, "SubStream2" ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/RegressionTest_i46848.java b/package/qa/storages/RegressionTest_i46848.java
new file mode 100644
index 0000000000..9252766abc
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i46848.java
@@ -0,0 +1,209 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i46848 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i46848( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i46848: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType2",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // create a new storage based on the stream and change the mediatype of the substorage
+ // as described in the bug description
+
+
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "ChangedMediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose the temporary storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // create a new readonly storage based on the stream and check the contents
+
+
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open the substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempStorage, "MediaType2", true, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempSubStorage, "ChangedMediaType3", false, ElementModes.READ ) )
+ return false;
+
+ // the MediaType and the contents must be up to date
+ if ( !m_aTestHelper.checkStream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i49755.java b/package/qa/storages/RegressionTest_i49755.java
new file mode 100644
index 0000000000..d2b32b8b93
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i49755.java
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i49755 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i49755( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i49755: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType1",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ byte pBytes[] = new byte[36000];
+ for ( int nInd = 0; nInd < 36000; nInd++ )
+ pBytes[nInd] = (byte)( nInd % 128 );
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType2",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // open a new substorage
+ XStorage xTempSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubStorage2",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubSubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubSubStorage, "SubStream1", "MediaType4", true, pBytes ) )
+ return false;
+
+ // open a new substorage
+ XStorage xTempSubStorage1 = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage3",
+ ElementModes.WRITE );
+ if ( xTempSubStorage1 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage1,
+ "MediaType5",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage1, "SubStream2", "MediaType4", false, pBytes ) )
+ return false;
+
+
+ // commit substorages first
+ if ( !m_aTestHelper.commitStorage( xTempSubSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage1 ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // now change the contents of the second substorage
+ // without changing of the contents of the first substorage
+
+
+ Object oStep2TempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xStep2TempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oStep2TempStorage );
+ if ( xStep2TempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ XStorage xStep2TempSubStorage1 = m_aTestHelper.openSubStorage( xStep2TempStorage,
+ "SubStorage3",
+ ElementModes.WRITE );
+ if ( xStep2TempSubStorage1 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xStep2TempSubStorage1, "SubStream2", "MediaType4", false, pBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xStep2TempSubStorage1 ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xStep2TempStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xStep2TempStorage ) )
+ return false;
+
+
+
+ // now check all the written information
+ // and the raw stream contents
+
+
+ // close the output part of the temporary stream
+ // the output part must present since we already wrote to the stream
+ if ( !m_aTestHelper.closeOutput( xTempFileStream ) )
+ return false;
+
+ XInputStream xTempInStream = m_aTestHelper.getInputStream( xTempFileStream );
+ if ( xTempInStream == null )
+ return false;
+
+ // open input stream
+ // since no mode is provided the result storage must be opened readonly
+ Object pOneArg[] = new Object[1];
+ pOneArg[0] = (Object) xTempInStream;
+
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pOneArg );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open storage based on input stream!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType1", true, ElementModes.READ ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage, "MediaType2", false, ElementModes.READ ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubSubStorage = m_aTestHelper.openSubStorage( xResultSubStorage,
+ "SubStorage2",
+ ElementModes.READ );
+ if ( xResultSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubSubStorage, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubSubStorage, "SubStream1", "MediaType4", true, pBytes ) )
+ return false;
+
+
+
+ XStorage xResultSubStorage1 = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage3",
+ ElementModes.READ );
+ if ( xResultSubStorage1 == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage1, "MediaType5", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage1, "SubStream2", "MediaType4", false, pBytes ) )
+ return false;
+
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/RegressionTest_i55821.java b/package/qa/storages/RegressionTest_i55821.java
new file mode 100644
index 0000000000..b4be15db48
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i55821.java
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i55821 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i55821( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i55821: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+
+ // create a temporary stream and a storage based on it
+ // fill the storage with the data that will be used for testing
+
+
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ String sPass = "12345";
+ byte pBytes[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ // the stream will not be encrypted
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "SubStream1", "MediaType1", false, pBytes, sPass ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "SubStream2", "MediaType2", false, pBytes, sPass ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // reopen the target storage readonly, and check contents
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkEncrStream( xResultStorage, "SubStream1", "MediaType1", pBytes, sPass ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xResultStorage, "SubStream2", "MediaType2", pBytes, sPass ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i59886.java b/package/qa/storages/RegressionTest_i59886.java
new file mode 100644
index 0000000000..90bf87a7e3
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i59886.java
@@ -0,0 +1,261 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i59886 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i59886( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i59886: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ byte pBytes[] = new byte[36000];
+ for ( int nInd = 0; nInd < 36000; nInd++ )
+ pBytes[nInd] = (byte)( nInd % 128 );
+
+ String sPass = "12345";
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "SubStream1", "MediaType1", true, pBytes, sPass ) )
+ return false;
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempSubStorage, "SubStream2", "MediaType2", false, pBytes, sPass ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // now reopen the storage, set the common storage key
+ // and copy the storage
+
+
+ Object oStep2TempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xStep2TempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oStep2TempStorage );
+ if ( xStep2TempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+
+ XStorage xStep2TempSubStorage = m_aTestHelper.openSubStorage( xStep2TempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xStep2TempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // set the common storage password
+ XEncryptionProtectedSource xEncr = (XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xStep2TempStorage );
+ if ( xEncr == null )
+ {
+ m_aTestHelper.Error( "The storage does not support encryption access!" );
+ return false;
+ }
+ try
+ {
+ xEncr.setEncryptionPassword( sPass );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can not set the common storage password!" );
+ return false;
+ }
+
+ // open the stream for writing and read them so that the cache is created, but do not change
+ byte pDummyData[][] = new byte[1][3];
+ XStream xTempStream1 = m_aTestHelper.OpenStream( xStep2TempStorage, "SubStream1", ElementModes.WRITE );
+ XStream xTempStream2 = m_aTestHelper.OpenStream( xStep2TempSubStorage, "SubStream2", ElementModes.WRITE );
+ if ( xTempStream1 == null || xTempStream2 == null )
+ return false;
+
+ XInputStream xTempInStream1 = xTempStream1.getInputStream();
+ XInputStream xTempInStream2 = xTempStream2.getInputStream();
+ if ( xTempInStream1 == null || xTempInStream2 == null )
+ {
+ m_aTestHelper.Error( "No input stream is available!" );
+ return false;
+ }
+
+ xTempInStream1.readBytes( pDummyData, 3 );
+ xTempInStream2.readBytes( pDummyData, 3 );
+
+
+ // create temporary storage, it will be checked later
+ Object oTargetStorage = m_xStorageFactory.createInstance();
+ XStorage xTargetStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTargetStorage );
+ if ( xTargetStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // copy the current storage to the target
+ try
+ {
+ xStep2TempStorage.copyToStorage( xTargetStorage );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can not copy the storage with common storage password!" );
+ return false;
+ }
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xStep2TempStorage ) )
+ return false;
+
+
+ // now check all the information in the copy
+
+
+ if ( !m_aTestHelper.checkStorageProperties( xTargetStorage, "MediaType3", true, ElementModes.WRITE ) )
+ return false;
+
+ // open existing substorage
+ XStorage xTargetSubStorage = m_aTestHelper.openSubStorage( xTargetStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTargetSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xTargetSubStorage, "MediaType4", false, ElementModes.WRITE ) )
+ return false;
+
+ // set the common storage password
+ XEncryptionProtectedSource xTargetEncr = (XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xTargetStorage );
+ if ( xTargetEncr == null )
+ {
+ m_aTestHelper.Error( "The storage does not support encryption access!" );
+ return false;
+ }
+ try
+ {
+ xTargetEncr.setEncryptionPassword( sPass );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can not set the common storage password!" );
+ return false;
+ }
+
+ // check the streams
+ if ( !m_aTestHelper.checkStream( xTargetStorage, "SubStream1", "MediaType1", true, pBytes ) )
+ return false;
+ if ( !m_aTestHelper.checkStream( xTargetSubStorage, "SubStream2", "MediaType2", true, pBytes ) )
+ return false;
+
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTargetStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i61909.java b/package/qa/storages/RegressionTest_i61909.java
new file mode 100644
index 0000000000..a73d8677f2
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i61909.java
@@ -0,0 +1,185 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import java.net.URI;
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipEntry;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i61909 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i61909( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i61909: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ byte pBytes[] = new byte[36000];
+ for ( int nInd = 0; nInd < 36000; nInd++ )
+ pBytes[nInd] = (byte)( nInd % 128 );
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "SubStream1", "MediaType1", true, pBytes ) )
+ return false;
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream2", "MediaType2", true, pBytes ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // now reopen the storage, and insert a new stream
+
+
+ Object oStep2TempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xStep2TempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oStep2TempStorage );
+ if ( xStep2TempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xStep2TempStorage, "SubStream3", "MediaType5", true, pBytes ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xStep2TempStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xStep2TempStorage ) )
+ return false;
+
+
+ // now access the stream using ZipInputStream
+
+
+ URI aUri = new URI( sTempFileURL );
+ File aFile = new File( aUri );
+ FileInputStream aFileStream = new FileInputStream( aFile );
+ ZipInputStream aZipStream = new ZipInputStream( aFileStream );
+
+ ZipEntry aEntry;
+ int nNumber = 0;
+ m_aTestHelper.Message( "Available entries:" );
+ while ( ( aEntry = aZipStream.getNextEntry() ) != null )
+ {
+ nNumber++;
+ m_aTestHelper.Message( aEntry.getName() );
+ }
+
+ if ( nNumber != 6 )
+ {
+ m_aTestHelper.Error( "Wrong number of entries: " + nNumber + ", Expected: 6" );
+ return false;
+ }
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/RegressionTest_i84234.java b/package/qa/storages/RegressionTest_i84234.java
new file mode 100644
index 0000000000..7bd8073c64
--- /dev/null
+++ b/package/qa/storages/RegressionTest_i84234.java
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class RegressionTest_i84234 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public RegressionTest_i84234( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "RegressionTest_i84234: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "SubStream1", "text/xml", false, pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream2", "text/xml", false, pBytes1 ) )
+ return false;
+
+
+
+ // commit the storages and dispose them
+
+
+ // commit substorage
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // commit storage
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose storage
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // reopen the storages in readwrite mode and check Compressed flag
+
+
+ oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStream( xTempStorage, "SubStream1", "text/xml", false, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xTempSubStorage, "SubStream2", "text/xml", false, pBytes1 ) )
+ return false;
+
+ // the root storage is based on the temporary stream so it can be left undisposed, since it does not lock
+ // any resource, later the garbage collector will release the object and it must die by refcount
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/StorageTest.java b/package/qa/storages/StorageTest.java
new file mode 100644
index 0000000000..e35fd55993
--- /dev/null
+++ b/package/qa/storages/StorageTest.java
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+public interface StorageTest
+{
+ boolean test();
+}
+
diff --git a/package/qa/storages/StorageUnitTest.java b/package/qa/storages/StorageUnitTest.java
new file mode 100644
index 0000000000..a8e433c3ee
--- /dev/null
+++ b/package/qa/storages/StorageUnitTest.java
@@ -0,0 +1,319 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+package complex.storages;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XMultiComponentFactory;
+import com.sun.star.connection.XConnector;
+import com.sun.star.connection.XConnection;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.uno.XNamingService;
+import com.sun.star.uno.XComponentContext;
+
+import com.sun.star.container.*;
+import com.sun.star.beans.*;
+import com.sun.star.lang.*;
+
+import complexlib.ComplexTestCase;
+
+import complex.storages.*;
+
+import util.utils;
+import java.util.*;
+import java.io.*;
+
+/* This unit test for storage objects is designed to
+ * test most important statements from storage service
+ * specification.
+ *
+ * Regression tests are added to extend the tested
+ * functionalities.
+ */
+public class StorageUnitTest extends ComplexTestCase
+{
+ private XMultiServiceFactory m_xMSF = null;
+ private XSingleServiceFactory m_xStorageFactory = null;
+
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "ExecuteTest01",
+ "ExecuteTest02",
+ "ExecuteTest03",
+ "ExecuteTest04",
+ "ExecuteTest05",
+ "ExecuteTest06",
+ "ExecuteTest07",
+ "ExecuteTest08",
+ "ExecuteTest09",
+ "ExecuteTest10",
+ "ExecuteTest11",
+ "ExecuteTest12",
+ "ExecuteTest13",
+ "ExecuteTest14",
+ "ExecuteTest15",
+ "ExecuteTest16",
+ "ExecuteTest17",
+ "ExecuteTest18",
+ "ExecuteRegressionTest_114358",
+ "ExecuteRegressionTest_i29169",
+ "ExecuteRegressionTest_i30400",
+ "ExecuteRegressionTest_i29321",
+ "ExecuteRegressionTest_i30677",
+ "ExecuteRegressionTest_i27773",
+ "ExecuteRegressionTest_i46848",
+ "ExecuteRegressionTest_i55821",
+ "ExecuteRegressionTest_i35095",
+ "ExecuteRegressionTest_i49755",
+ "ExecuteRegressionTest_i59886",
+ "ExecuteRegressionTest_i61909",
+ "ExecuteRegressionTest_i84234",
+ "ExecuteRegressionTest_125919"
+ };
+ }
+
+ public String getTestObjectName()
+ {
+ return "StorageUnitTest";
+ }
+
+ public void before()
+ {
+ m_xMSF = (XMultiServiceFactory)param.getMSF();
+ if ( m_xMSF == null )
+ {
+ failed( "Can't create service factory!" );
+ return;
+ }
+
+ try {
+ Object oStorageFactory = m_xMSF.createInstance( "com.sun.star.embed.StorageFactory" );
+ m_xStorageFactory = (XSingleServiceFactory)UnoRuntime.queryInterface( XSingleServiceFactory.class,
+ oStorageFactory );
+ }
+ catch( Exception e )
+ {
+ failed( "Can't create storage factory!" );
+ return;
+ }
+
+ if ( m_xStorageFactory == null )
+ {
+ failed( "Can't create service factory!" );
+ return;
+ }
+ }
+
+ public void ExecuteTest01()
+ {
+ StorageTest aTest = new Test01( m_xMSF, m_xStorageFactory, log );
+ assure( "Test01 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest02()
+ {
+ StorageTest aTest = new Test02( m_xMSF, m_xStorageFactory, log );
+ assure( "Test02 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest03()
+ {
+ StorageTest aTest = new Test03( m_xMSF, m_xStorageFactory, log );
+ assure( "Test03 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest04()
+ {
+ StorageTest aTest = new Test04( m_xMSF, m_xStorageFactory, log );
+ assure( "Test04 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest05()
+ {
+ StorageTest aTest = new Test05( m_xMSF, m_xStorageFactory, log );
+ assure( "Test05 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest06()
+ {
+ StorageTest aTest = new Test06( m_xMSF, m_xStorageFactory, log );
+ assure( "Test06 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest07()
+ {
+ StorageTest aTest = new Test07( m_xMSF, m_xStorageFactory, log );
+ assure( "Test07 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest08()
+ {
+ StorageTest aTest = new Test08( m_xMSF, m_xStorageFactory, log );
+ assure( "Test08 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest09()
+ {
+ StorageTest aTest = new Test09( m_xMSF, m_xStorageFactory, log );
+ assure( "Test09 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest10()
+ {
+ StorageTest aTest = new Test10( m_xMSF, m_xStorageFactory, log );
+ assure( "Test10 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest11()
+ {
+ StorageTest aTest = new Test11( m_xMSF, m_xStorageFactory, log );
+ assure( "Test11 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest12()
+ {
+ StorageTest aTest = new Test12( m_xMSF, m_xStorageFactory, log );
+ assure( "Test12 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest13()
+ {
+ StorageTest aTest = new Test13( m_xMSF, m_xStorageFactory, log );
+ assure( "Test13 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest14()
+ {
+ StorageTest aTest = new Test14( m_xMSF, m_xStorageFactory, log );
+ assure( "Test14 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest15()
+ {
+ StorageTest aTest = new Test15( m_xMSF, m_xStorageFactory, log );
+ assure( "Test15 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest16()
+ {
+ StorageTest aTest = new Test16( m_xMSF, m_xStorageFactory, log );
+ assure( "Test16 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest17()
+ {
+ StorageTest aTest = new Test17( m_xMSF, m_xStorageFactory, log );
+ assure( "Test17 failed!", aTest.test() );
+ }
+
+ public void ExecuteTest18()
+ {
+ StorageTest aTest = new Test18( m_xMSF, m_xStorageFactory, log );
+ assure( "Test18 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_114358()
+ {
+ StorageTest aTest = new RegressionTest_114358( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_114358 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i29169()
+ {
+ StorageTest aTest = new RegressionTest_i29169( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i29169 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i30400()
+ {
+ StorageTest aTest = new RegressionTest_i30400( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i30400 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i29321()
+ {
+ StorageTest aTest = new RegressionTest_i29321( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i29321 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i30677()
+ {
+ StorageTest aTest = new RegressionTest_i30677( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i30677 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i27773()
+ {
+ StorageTest aTest = new RegressionTest_i27773( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i27773 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i46848()
+ {
+ StorageTest aTest = new RegressionTest_i46848( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i46848 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i55821()
+ {
+ StorageTest aTest = new RegressionTest_i55821( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i55821 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i35095()
+ {
+ StorageTest aTest = new RegressionTest_i35095( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i35095 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i49755()
+ {
+ StorageTest aTest = new RegressionTest_i49755( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i49755 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i59886()
+ {
+ StorageTest aTest = new RegressionTest_i59886( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i59886 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i61909()
+ {
+ StorageTest aTest = new RegressionTest_i61909( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i61909 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_i84234()
+ {
+ StorageTest aTest = new RegressionTest_i84234( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_i84234 failed!", aTest.test() );
+ }
+
+ public void ExecuteRegressionTest_125919()
+ {
+ StorageTest aTest = new RegressionTest_125919( m_xMSF, m_xStorageFactory, log );
+ assure( "RegressionTest_125919 failed!", aTest.test() );
+ }
+}
+
diff --git a/package/qa/storages/Test01.java b/package/qa/storages/Test01.java
new file mode 100644
index 0000000000..a5d76bcca0
--- /dev/null
+++ b/package/qa/storages/Test01.java
@@ -0,0 +1,195 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test01 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test01( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test01: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "BigSubStream2", "MediaType2", false, pBigBytes ) )
+ return false;
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream2", "MediaType2", false, pBytes2 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // copy xTempStorage to xTempFileStorage
+ // xTempFileStorage will be automatically committed
+ if ( !m_aTestHelper.copyStorage( xTempStorage, xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) || !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now check all the written and copied information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType3", true, ElementModes.WRITE ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "BigSubStream2", "MediaType2", false, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream2", "MediaType2", false, pBytes2 ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test02.java b/package/qa/storages/Test02.java
new file mode 100644
index 0000000000..2e3e712549
--- /dev/null
+++ b/package/qa/storages/Test02.java
@@ -0,0 +1,181 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test02 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test02( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test02: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType2",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+
+ // now check all the written information
+
+
+ // close the output part of the temporary stream
+ // the output part must present since we already wrote to the stream
+ if ( !m_aTestHelper.closeOutput( xTempFileStream ) )
+ return false;
+
+ XInputStream xTempInStream = m_aTestHelper.getInputStream( xTempFileStream );
+ if ( xTempInStream == null )
+ return false;
+
+
+ // open input stream
+ // since no mode is provided the result storage must be opened readonly
+ Object pOneArg[] = new Object[1];
+ pOneArg[0] = (Object) xTempInStream;
+
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pOneArg );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open storage based on input stream!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType2", true, ElementModes.READ ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test03.java b/package/qa/storages/Test03.java
new file mode 100644
index 0000000000..8e7c1ee304
--- /dev/null
+++ b/package/qa/storages/Test03.java
@@ -0,0 +1,249 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+import com.sun.star.container.XNameAccess;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test03 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test03( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test03: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "BigSubStream2", "MediaType2", false, pBigBytes ) )
+ return false;
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream2", "MediaType2", false, pBytes2 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+
+ // check storage hierarchy tree
+
+
+ // check that isStorageElement() and isStreamElement reacts to nonexistent object correctly
+ try {
+ xTempStorage.isStorageElement( "does not exist" );
+ m_aTestHelper.Error( "Nonexistent element doesn't detected by isStorageElement() call!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by isStorageElement() call: " + e );
+ return false;
+ }
+
+ try {
+ xTempStorage.isStreamElement( "does not exist" );
+ m_aTestHelper.Error( "Nonexistent element doesn't detected by isStreamElement() call!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by isStreamElement() call: " + e );
+ return false;
+ }
+
+ XNameAccess xRootNameAccess = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xTempStorage );
+ if ( xRootNameAccess == null )
+ {
+ m_aTestHelper.Error( "Root storage doesn't support XNameAccess!" );
+ return false;
+ }
+
+ try {
+ if ( !xTempStorage.isStorageElement( "SubStorage1" ) || xTempStorage.isStreamElement( "SubStorage1" ) )
+ {
+ m_aTestHelper.Error( "Child 'SubStorage1' can not be detected as storage!" );
+ return false;
+ }
+
+ if ( xTempStorage.isStorageElement( "SubStream1" ) || !xTempStorage.isStreamElement( "SubStream1" ) )
+ {
+ m_aTestHelper.Error( "Child 'SubStream1' can not be detected as stream!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Child's type can not be detected, exception: " + e );
+ return false;
+ }
+
+
+ // check that root storage contents are represented correctly
+ String sRootCont[] = xRootNameAccess.getElementNames();
+
+ if ( sRootCont.length != 3 )
+ {
+ m_aTestHelper.Error( "Root storage contains wrong amount of children!" );
+ return false;
+ }
+
+ int nFlag = 0;
+ for ( int nInd = 0; nInd < sRootCont.length; nInd++ )
+ {
+ if ( sRootCont[nInd].equals( "SubStorage1" ) )
+ nFlag |= 1;
+ else if ( sRootCont[nInd].equals( "SubStream1" ) )
+ nFlag |= 2;
+ else if ( sRootCont[nInd].equals( "BigSubStream1" ) )
+ nFlag |= 4;
+ }
+
+ if ( nFlag != 7 || !( xRootNameAccess.hasByName( "BigSubStream1" ) && xRootNameAccess.hasByName( "SubStream1" ) && xRootNameAccess.hasByName( "SubStorage1" ) ) )
+ {
+ m_aTestHelper.Error( "Root storage contains wrong list of children!" );
+ return false;
+ }
+
+ // get storage through XNameAccess
+ XStorage xResultSubStorage = getStorageFromNameAccess( xRootNameAccess, "SubStorage1" );
+ if ( xResultSubStorage == null )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ XNameAccess xChildAccess = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xResultSubStorage );
+ if ( xChildAccess == null )
+ {
+ m_aTestHelper.Error( "Child storage doesn't support XNameAccess!" );
+ return false;
+ }
+
+ if ( !( xChildAccess.hasByName( "SubStream2" ) && xChildAccess.hasByName( "BigSubStream2" ) )
+ || !xResultSubStorage.isStreamElement( "SubStream2" )
+ || !xResultSubStorage.isStreamElement( "BigSubStream2" ) )
+ {
+ m_aTestHelper.Error( "'SubStream2' can not be detected as child stream element of 'SubStorage1'!" );
+ return false;
+ }
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+ public XStorage getStorageFromNameAccess( XNameAccess xAccess, String sName )
+ {
+ try
+ {
+ Object oStorage = xAccess.getByName( sName );
+ XStorage xResult = (XStorage) UnoRuntime.queryInterface( XStorage.class, oStorage );
+
+ if ( xResult != null )
+ return xResult;
+ else
+ m_aTestHelper.Error( "Can't retrieve substorage '" + sName + "' through XNameAccess!" );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can't retrieve substorage '" + sName + "' through XNameAccess, exception: " + e );
+ }
+
+ return null;
+ }
+
+}
+
diff --git a/package/qa/storages/Test04.java b/package/qa/storages/Test04.java
new file mode 100644
index 0000000000..78af017897
--- /dev/null
+++ b/package/qa/storages/Test04.java
@@ -0,0 +1,325 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+import com.sun.star.lang.DisposedException;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.container.XNameAccess;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test04 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test04( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test04: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open substorages and create streams there
+
+ // first substorage of the root storage
+ XStorage xTempSubStorage1 = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage1 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage1, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage1, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // second substorage of the root storage
+ XStorage xTempSubStorage2 = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage2",
+ ElementModes.WRITE );
+ if ( xTempSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage2, "BigSubStream2", "MediaType2", false, pBigBytes ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage2, "SubStream2", "MediaType2", false, pBytes2 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage1,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage2,
+ "MediaType5",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.copyElementTo( xTempStorage, "SubStorage1", xTempFileStorage ) )
+ return false;
+
+ // if storage is not committed before disposing all the changes will be lost
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage2 ) )
+ return false;
+
+ // a storage must be disposed before moving/removing otherwise the access will be denied
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage2 ) )
+ return false;
+
+ if ( !m_aTestHelper.moveElementTo( xTempStorage, "SubStorage2", xTempFileStorage ) )
+ return false;
+
+ // SubStorage2 must be removed and disposed now
+ try
+ {
+ xTempSubStorage2.isStreamElement( "SubStream2" );
+ m_aTestHelper.Error( "SubStorage2 must be disposed already!" );
+ return false;
+ }
+ catch( com.sun.star.lang.DisposedException de )
+ {
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception in case of disposed storage, exception: " + e );
+ return false;
+ }
+
+ if ( !m_aTestHelper.copyElementTo( xTempSubStorage1, "SubStream1", xTempFileStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.renameElement( xTempFileStorage, "SubStream1", "SubStream1_copy" ) )
+ return false;
+
+ if ( !m_aTestHelper.moveElementTo( xTempSubStorage1, "SubStream1", xTempFileStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.copyElementTo( xTempSubStorage1, "BigSubStream1", xTempFileStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.renameElement( xTempFileStorage, "BigSubStream1", "BigSubStream1_copy" ) )
+ return false;
+
+ if ( !m_aTestHelper.moveElementTo( xTempSubStorage1, "BigSubStream1", xTempFileStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) || !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now check all the written and copied information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+ Object oResStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResStorage );
+ if ( xResStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ // open and check SubStorage1
+ XStorage xResSubStorage1 = m_aTestHelper.openSubStorage( xResStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResSubStorage1 == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResSubStorage1, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+
+ // open and check SubStorage2
+ XStorage xResSubStorage2 = m_aTestHelper.openSubStorage( xResStorage,
+ "SubStorage2",
+ ElementModes.READ );
+ if ( xResSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResSubStorage2, "MediaType5", false, ElementModes.READ ) )
+ return false;
+
+
+ // check all the result streams
+
+ if ( !m_aTestHelper.checkStream( xResStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResStorage, "SubStream1_copy", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResStorage, "BigSubStream1_copy", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubStorage1, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubStorage1, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubStorage2, "SubStream2", "MediaType2", false, pBytes2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubStorage2, "BigSubStream2", "MediaType2", false, pBigBytes ) )
+ return false;
+
+ // the storage must be disposed before removing
+ if ( !m_aTestHelper.disposeStorage( xResSubStorage2 ) )
+ return false;
+
+ // remove element and check that it was removed completely
+ if ( !m_aTestHelper.removeElement( xResStorage, "SubStorage2" ) )
+ return false;
+
+ try
+ {
+ XNameAccess xResAccess = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xResStorage );
+ if ( xResAccess.hasByName( "SubStorage2" ) )
+ m_aTestHelper.Error( "SubStorage2 must be removed already!" );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can't get access to root storage, exception: " + e );
+ return false;
+ }
+
+ try
+ {
+ xResSubStorage2.isStreamElement( "SubStream2" );
+
+ m_aTestHelper.Error( "SubStorage2 must be disposed already!" );
+ return false;
+ }
+ catch( com.sun.star.lang.DisposedException de )
+ {
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception in case of disposed storage, exception: " + e );
+ return false;
+ }
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test05.java b/package/qa/storages/Test05.java
new file mode 100644
index 0000000000..9138b4f86b
--- /dev/null
+++ b/package/qa/storages/Test05.java
@@ -0,0 +1,317 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test05 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test05( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test05: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempFileStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xSubSubStorage = m_aTestHelper.openSubStorage( xTempSubStorage,
+ "SubSubStorage1",
+ ElementModes.WRITE );
+ if ( xSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xSubSubStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xSubSubStorage, "BigSubStream2", "MediaType2", false, pBigBytes ) )
+ return false;
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xSubSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xSubSubStorage, "SubStream2", "MediaType2", false, pBytes2 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempFileStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xSubSubStorage,
+ "MediaType5",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // commit all the storages
+ if ( !m_aTestHelper.commitStorage( xSubSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // try to open an opened substorage, open call must fail
+ if ( !m_aTestHelper.cantOpenStorage( xTempFileStorage, "SubStorage1" ) )
+ return false;
+
+
+ // reopen created streams
+ XStream xSubStream1 = m_aTestHelper.OpenStream( xSubSubStorage,
+ "SubStream1",
+ ElementModes.WRITE | ElementModes.NOCREATE );
+ XStream xBigSubStream1 = m_aTestHelper.OpenStream( xSubSubStorage,
+ "BigSubStream1",
+ ElementModes.WRITE | ElementModes.NOCREATE );
+ XStream xSubStream2 = m_aTestHelper.OpenStream( xSubSubStorage,
+ "SubStream2",
+ ElementModes.READ | ElementModes.NOCREATE );
+ XStream xBigSubStream2 = m_aTestHelper.OpenStream( xSubSubStorage,
+ "BigSubStream2",
+ ElementModes.READ | ElementModes.NOCREATE );
+
+ if ( xSubStream1 == null || xBigSubStream1 == null || xSubStream2 == null || xBigSubStream2 == null )
+ return false;
+
+ // it should be possible to have more than one copy of stream for reading
+ XStream xSubStream2clone = m_aTestHelper.OpenStream( xSubSubStorage,
+ "SubStream2",
+ ElementModes.READ | ElementModes.NOCREATE );
+ XStream xBigSubStream2clone = m_aTestHelper.OpenStream( xSubSubStorage,
+ "BigSubStream2",
+ ElementModes.READ | ElementModes.NOCREATE );
+ if ( xSubStream2clone == null || xBigSubStream2clone == null )
+ return false;
+
+
+ // so now the first streams can not be open neither for reading nor for writing
+ if ( !m_aTestHelper.cantOpenStream( xSubSubStorage, "SubStream1", ElementModes.WRITE )
+ || !m_aTestHelper.cantOpenStream( xSubSubStorage, "SubStream1", ElementModes.READ )
+ || !m_aTestHelper.cantOpenStream( xSubSubStorage, "BigSubStream1", ElementModes.WRITE )
+ || !m_aTestHelper.cantOpenStream( xSubSubStorage, "BigSubStream1", ElementModes.READ ) )
+ return false;
+
+ // the second streams can not be open for writing
+ if ( !m_aTestHelper.cantOpenStream( xSubSubStorage, "SubStream2", ElementModes.WRITE )
+ || !m_aTestHelper.cantOpenStream( xSubSubStorage, "BigSubStream2", ElementModes.WRITE ) )
+ return false;
+
+
+ // dispose xTestSubStorage, all the subtree must be disposed
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+ // check that subtree was disposed correctly
+ try
+ {
+ xSubSubStorage.isStreamElement( "SubStream1" );
+ m_aTestHelper.Error( "Substorage was not disposed!" );
+ return false;
+ }
+ catch ( com.sun.star.lang.DisposedException de )
+ {}
+ catch ( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by disposed storage: " + e );
+ return false;
+ }
+
+ try
+ {
+ xSubStream1.getInputStream();
+ m_aTestHelper.Error( "Writeable substream was not disposed!" );
+ return false;
+ }
+ catch ( com.sun.star.lang.DisposedException de )
+ {}
+ catch ( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by disposed stream: " + e );
+ return false;
+ }
+
+ try
+ {
+ xSubStream2.getInputStream();
+ m_aTestHelper.Error( "Readonly substream was not disposed!" );
+ return false;
+ }
+ catch ( com.sun.star.lang.DisposedException de )
+ {}
+ catch ( Exception e )
+ {
+ m_aTestHelper.Error( "Wrong exception is thrown by disposed stream: " + e );
+ return false;
+ }
+
+
+ // dispose root storage
+ if ( !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+
+ // now check all the written and copied information
+
+
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType3", true, ElementModes.READ ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage 'SubSubStorage'!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResSubStorage, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResSubSubStorage = m_aTestHelper.openSubStorage( xResSubStorage,
+ "SubSubStorage1",
+ ElementModes.READ );
+ if ( xResSubSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage 'SubSubStorage'!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResSubSubStorage, "MediaType5", false, ElementModes.READ ) )
+ return false;
+
+ // check substreams
+ if ( !m_aTestHelper.checkStream( xResSubSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubSubStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubSubStorage, "SubStream2", "MediaType2", false, pBytes2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResSubSubStorage, "BigSubStream2", "MediaType2", false, pBigBytes ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test06.java b/package/qa/storages/Test06.java
new file mode 100644
index 0000000000..7d379f1823
--- /dev/null
+++ b/package/qa/storages/Test06.java
@@ -0,0 +1,297 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.lang.IllegalArgumentException;
+import com.sun.star.container.NoSuchElementException;
+import com.sun.star.container.ElementExistException;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test06 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test06( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test06: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ try
+ {
+ xTempStorage.copyToStorage( null );
+ m_aTestHelper.Error( "The method must throw an exception because of illegal parameter!" );
+ return false;
+ }
+ catch( com.sun.star.lang.IllegalArgumentException iae )
+ {}
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception because of illegal parameter : " + e );
+ return false;
+ }
+
+ // open new substorages
+ XStorage xTempSubStorage1 = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ XStorage xTempSubStorage2 = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage2",
+ ElementModes.WRITE );
+ if ( xTempSubStorage1 == null || xTempSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // in case stream is open for reading it must exist
+ try
+ {
+ xTempSubStorage1.openStreamElement( "NonExistingStream", ElementModes.READ );
+ m_aTestHelper.Error( "The method must throw an exception in case of try to open nonexistent stream for reading!" );
+ return false;
+ }
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to open nonexistent stream for reading : " + e );
+ return false;
+ }
+
+ // in case a storage is open for reading it must exist
+ try
+ {
+ xTempSubStorage1.openStreamElement( "NonExistingStorage", ElementModes.READ );
+ m_aTestHelper.Error( "The method must throw an exception in case of try to open nonexistent storage for reading!" );
+ return false;
+ }
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to open nonexistent storage for reading : " + e );
+ return false;
+ }
+
+ // in case of removing nonexistent element an exception must be thrown
+ try
+ {
+ xTempSubStorage1.removeElement( "NonExistingElement" );
+ m_aTestHelper.Error( "An exception must be thrown in case of removing nonexistent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to remove nonexistent element : " + e );
+ return false;
+ }
+
+ // in case of renaming of nonexistent element an exception must be thrown
+ try
+ {
+ xTempSubStorage1.renameElement( "NonExistingElement", "NewName" );
+ m_aTestHelper.Error( "An exception must be thrown in case of renaming nonexistent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to rename nonexistent element : " + e );
+ return false;
+ }
+
+ // in case of renaming to a name of existent element an exception must be thrown
+ try
+ {
+ xTempStorage.renameElement( "SubStorage1", "SubStorage2" );
+ m_aTestHelper.Error( "An exception must be thrown in case of renaming to the name of existent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.ElementExistException ee )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of try to rename to the name of existent element : " + e );
+ return false;
+ }
+
+ // in case of copying target storage must be provided
+ try
+ {
+ xTempStorage.copyElementTo( "SubStorage1", null, "SubStorage1" );
+ m_aTestHelper.Error( "An exception must be thrown in case empty reference is provided as target for copying!" );
+ return false;
+ }
+ catch( com.sun.star.lang.IllegalArgumentException iae )
+ {}
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case empty reference is provided as target for copying : " + e );
+ return false;
+ }
+
+ // in case of moving target storage must be provided
+ try
+ {
+ xTempStorage.moveElementTo( "SubStorage1", null, "SubStorage1" );
+ m_aTestHelper.Error( "An exception must be thrown in case empty reference is provided as target for moving!" );
+ return false;
+ }
+ catch( com.sun.star.lang.IllegalArgumentException iae )
+ {}
+ catch( com.sun.star.uno.Exception ue )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case empty reference is provided as target for moving : " + e );
+ return false;
+ }
+
+
+ // prepare target for further testings
+
+ // create new temporary storage based on arbitrary medium
+ Object oTargetStorage = m_xStorageFactory.createInstance();
+ XStorage xTargetStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTargetStorage );
+ if ( xTargetStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTargetSubStorage = m_aTestHelper.openSubStorage( xTargetStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTargetSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // in case of copying of nonexistent element an exception must be thrown
+ try
+ {
+ xTempStorage.copyElementTo( "Nonexistent element", xTargetStorage, "Target" );
+ m_aTestHelper.Error( "An exception must be thrown in case of copying of nonexistent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of copying of nonexistent element: " + e );
+ return false;
+ }
+
+ // in case of moving of nonexistent element an exception must be thrown
+ try
+ {
+ xTempStorage.moveElementTo( "Nonexistent element", xTargetStorage, "Target" );
+ m_aTestHelper.Error( "An exception must be thrown in case of moving of nonexistent element!" );
+ return false;
+ }
+ catch( com.sun.star.container.NoSuchElementException ne )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case of moving of nonexistent element: " + e );
+ return false;
+ }
+
+ // in case target for copying already exists an exception must be thrown
+ try
+ {
+ xTempStorage.copyElementTo( "SubStorage1", xTargetStorage, "SubStorage1" );
+ m_aTestHelper.Error( "An exception must be thrown in case target for copying already exists!" );
+ return false;
+ }
+ catch( com.sun.star.container.ElementExistException ee )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case target for copying already exists: " + e );
+ return false;
+ }
+
+ // in case target for moving already exists an exception must be thrown
+ try
+ {
+ xTempStorage.moveElementTo( "SubStorage1", xTargetStorage, "SubStorage1" );
+ m_aTestHelper.Error( "An exception must be thrown in case target for moving already exists!" );
+ return false;
+ }
+ catch( com.sun.star.container.ElementExistException ee )
+ {}
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Unexpected exception in case target for moving already exists: " + e );
+ return false;
+ }
+
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test07.java b/package/qa/storages/Test07.java
new file mode 100644
index 0000000000..db0cca5b03
--- /dev/null
+++ b/package/qa/storages/Test07.java
@@ -0,0 +1,180 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test07 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test07( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test07: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+ String sPass1 = "12345";
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "BigSubStream1", "MediaType1", true, pBigBytes, sPass1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "SubStream1", "MediaType1", true, pBytes1, sPass1 ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+ String sPass2 = "54321";
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "BigSubStream2", "MediaType2", false, pBigBytes, sPass2 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "SubStream2", "MediaType2", false, pBytes2, sPass2 ) )
+ return false;
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // copy xTempStorage to xTempFileStorage
+ // xTempFileStorage will be automatically committed
+ if ( !m_aTestHelper.copyStorage( xTempStorage, xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) || !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now check all the written and copied information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ Object o2CopyStorage = m_xStorageFactory.createInstance();
+ XStorage x2CopyStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, o2CopyStorage );
+ if ( x2CopyStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.copyStorage( xResultStorage, x2CopyStorage ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xResultStorage, "SubStream1", "MediaType1", pBytes1, sPass1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xResultStorage, "BigSubStream1", "MediaType1", pBigBytes, sPass1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xResultStorage, "SubStream2", "MediaType2", pBytes2, sPass2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xResultStorage, "BigSubStream2", "MediaType2", pBigBytes, sPass2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( x2CopyStorage, "SubStream1", "MediaType1", pBytes1, sPass1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( x2CopyStorage, "BigSubStream1", "MediaType1", pBigBytes, sPass1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( x2CopyStorage, "SubStream2", "MediaType2", pBytes2, sPass2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( x2CopyStorage, "BigSubStream2", "MediaType2", pBigBytes, sPass2 ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test08.java b/package/qa/storages/Test08.java
new file mode 100644
index 0000000000..9113ee3854
--- /dev/null
+++ b/package/qa/storages/Test08.java
@@ -0,0 +1,248 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test08 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test08( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test08: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // set the global password for the root storage
+ XEncryptionProtectedSource xTempStorageEncryption =
+ (XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xTempStorage );
+
+ if ( xTempStorageEncryption == null )
+ {
+ m_aTestHelper.Message( "Optional interface XEncryptionProtectedSource is not implemented, feature can not be tested!" );
+ return true;
+ }
+
+ String sPass1 = "123";
+ String sPass2 = "321";
+
+ try {
+ xTempStorageEncryption.setEncryptionPassword( sPass1 );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can't set a common encryption key for the storage, exception:" + e );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ // the stream will be encrypted with common password
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+ if ( !m_aTestHelper.WBToSubstrOfEncr( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1, true ) )
+ return false;
+ if ( !m_aTestHelper.WBToSubstrOfEncr( xTempSubStorage, "BigSubStream1", "MediaType1", true, pBigBytes, true ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ // the stream will not be encrypted
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+ if ( !m_aTestHelper.WBToSubstrOfEncr( xTempSubStorage, "SubStream2", "MediaType2", false, pBytes2, false ) )
+ return false;
+ if ( !m_aTestHelper.WBToSubstrOfEncr( xTempSubStorage, "BigSubStream2", "MediaType2", false, pBigBytes, false ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ // the stream will be compressed with own password
+ byte pBytes3[] = { 3, 3, 3, 3, 3 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ // the stream will not be encrypted
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempSubStorage, "SubStream3", "MediaType3", false, pBytes3, sPass2 ) )
+ return false;
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempSubStorage, "BigSubStream3", "MediaType3", false, pBigBytes, sPass2 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType4",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType5",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // create temporary file
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // copy xTempStorage to xTempFileStorage
+ // xTempFileStorage will be automatically committed
+ if ( !m_aTestHelper.copyStorage( xTempStorage, xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) || !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now check all the written and copied information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType4", true, ElementModes.READ ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage, "MediaType5", false, ElementModes.READ ) )
+ return false;
+
+ // set the global password for the root storage
+ XEncryptionProtectedSource xResultStorageEncryption =
+ (XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xResultStorage );
+
+ if ( xResultStorageEncryption == null )
+ {
+ m_aTestHelper.Error( "XEncryptionProtectedSource was successfully used already, so it must be supported!" );
+ return false;
+ }
+
+ try {
+ xResultStorageEncryption.setEncryptionPassword( sPass2 );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can't set a common encryption key for the storage, exception:" + e );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkEncrStream( xResultSubStorage, "SubStream1", "MediaType1", pBytes1, sPass1 ) )
+ return false;
+ if ( !m_aTestHelper.checkEncrStream( xResultSubStorage, "BigSubStream1", "MediaType1", pBigBytes, sPass1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream2", "MediaType2", false, pBytes2 ) )
+ return false;
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "BigSubStream2", "MediaType2", false, pBigBytes ) )
+ return false;
+
+ // the common root storage password should allow to open this stream
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream3", "MediaType3", true, pBytes3 ) )
+ return false;
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "BigSubStream3", "MediaType3", true, pBigBytes ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/Test09.java b/package/qa/storages/Test09.java
new file mode 100644
index 0000000000..3790ecd8b9
--- /dev/null
+++ b/package/qa/storages/Test09.java
@@ -0,0 +1,156 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test09 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test09( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test09: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ String sPass1 = "123";
+ String sPass2 = "321";
+ byte pBytes[] = { 1, 1, 1, 1, 1 };
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ // the stream will not be encrypted
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "SubStream1", "MediaType1", false, pBytes, sPass1 ) )
+ return false;
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "BigSubStream1", "MediaType1", false, pBigBytes, sPass1 ) )
+ return false;
+
+ // create temporary file
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // copy xTempStorage to xTempFileStorage
+ // xTempFileStorage will be automatically committed
+ if ( !m_aTestHelper.copyStorage( xTempStorage, xTempFileStorage ) )
+ return false;
+
+ // change password of the substream of new storage based on file
+ int nResult = m_aTestHelper.ChangeStreamPass( xTempFileStorage, "SubStream1", sPass1, sPass2 );
+ if ( nResult == 0 )
+ return false; // test failed
+ else if ( nResult == -1 )
+ return true; // tested optional feature is not supported
+
+ // change password of the substream of new storage based on file
+ nResult = m_aTestHelper.ChangeStreamPass( xTempFileStorage, "BigSubStream1", sPass1, sPass2 );
+ if ( nResult == 0 )
+ return false; // test failed
+ else if ( nResult == -1 )
+ return true; // tested optional feature is not supported
+
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) || !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now check all the written and copied information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkEncrStream( xResultStorage, "SubStream1", "MediaType1", pBytes, sPass2 ) )
+ return false;
+ if ( !m_aTestHelper.checkEncrStream( xResultStorage, "BigSubStream1", "MediaType1", pBigBytes, sPass2 ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/Test10.java b/package/qa/storages/Test10.java
new file mode 100644
index 0000000000..6b4a5bb411
--- /dev/null
+++ b/package/qa/storages/Test10.java
@@ -0,0 +1,250 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.container.XNameAccess;
+import com.sun.star.io.XStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test10 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test10( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test10: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream2", "MediaType2", true, pBytes2 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "BigSubStream2", "MediaType2", true, pBigBytes ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // check cloning at current state
+
+
+ // the new storage still was not committed so the clone must be empty
+ XStorage xClonedSubStorage = m_aTestHelper.cloneSubStorage( m_xStorageFactory, xTempStorage, "SubStorage1" );
+
+ if ( xClonedSubStorage == null )
+ {
+ m_aTestHelper.Error( "The result of clone is empty!" );
+ return false;
+ }
+
+ XNameAccess xClonedNameAccess = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xClonedSubStorage );
+ if ( xClonedNameAccess == null )
+ {
+ m_aTestHelper.Error( "XNameAccess is not implemented by the clone!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xClonedSubStorage, "", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( xClonedNameAccess.hasElements() )
+ {
+ m_aTestHelper.Error( "The new substorage still was not committed so it must be empty!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.disposeStorage( xClonedSubStorage ) )
+ return false;
+
+ xClonedSubStorage = null;
+ xClonedNameAccess = null;
+
+ // the new stream was opened, written and closed, that means flashed
+ // so the clone must contain all the information
+ XStream xClonedSubStream = m_aTestHelper.cloneSubStream( xTempStorage, "SubStream1" );
+ if ( !m_aTestHelper.InternalCheckStream( xClonedSubStream, "SubStream1", "MediaType1", true, pBytes1, true ) )
+ return false;
+
+ XStream xClonedBigSubStream = m_aTestHelper.cloneSubStream( xTempStorage, "BigSubStream1" );
+ if ( !m_aTestHelper.InternalCheckStream( xClonedBigSubStream, "BigSubStream1", "MediaType1", true, pBigBytes, true ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStream( xClonedSubStream, "SubStream1" ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStream( xClonedBigSubStream, "BigSubStream1" ) )
+ return false;
+
+
+ // commit substorage and check cloning
+
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ xClonedSubStorage = m_aTestHelper.cloneSubStorage( m_xStorageFactory, xTempStorage, "SubStorage1" );
+ if ( xClonedSubStorage == null )
+ {
+ m_aTestHelper.Error( "The result of clone is empty!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xClonedSubStorage, "MediaType4", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xClonedSubStorage, "SubStream2", "MediaType2", true, pBytes2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xClonedSubStorage, "BigSubStream2", "MediaType2", true, pBigBytes ) )
+ return false;
+
+ XStorage xCloneOfRoot = m_aTestHelper.cloneStorage( m_xStorageFactory, xTempStorage );
+ if ( xCloneOfRoot == null )
+ {
+ m_aTestHelper.Error( "The result of root clone is empty!" );
+ return false;
+ }
+
+ XNameAccess xCloneOfRootNA = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xCloneOfRoot );
+ if ( xCloneOfRootNA == null )
+ {
+ m_aTestHelper.Error( "XNameAccess is not implemented by the root clone!" );
+ return false;
+ }
+
+ if ( xCloneOfRootNA.hasElements() )
+ {
+ m_aTestHelper.Error( "The root storage still was not committed so it's clone must be empty!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.disposeStorage( xCloneOfRoot ) )
+ return false;
+
+ xCloneOfRoot = null;
+
+
+ // commit root storage and check cloning
+
+
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ xCloneOfRoot = m_aTestHelper.cloneStorage( m_xStorageFactory, xTempStorage );
+ if ( xCloneOfRoot == null )
+ {
+ m_aTestHelper.Error( "The result of root clone is empty!" );
+ return false;
+ }
+
+ XStorage xSubStorageOfClone = xCloneOfRoot.openStorageElement( "SubStorage1", ElementModes.READ );
+ if ( xSubStorageOfClone == null )
+ {
+ m_aTestHelper.Error( "The result of root clone is wrong!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xSubStorageOfClone, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xSubStorageOfClone, "SubStream2", "MediaType2", true, pBytes2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xSubStorageOfClone, "BigSubStream2", "MediaType2", true, pBigBytes ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/Test11.java b/package/qa/storages/Test11.java
new file mode 100644
index 0000000000..02320db81d
--- /dev/null
+++ b/package/qa/storages/Test11.java
@@ -0,0 +1,236 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.container.XNameAccess;
+import com.sun.star.io.XStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test11 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test11( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test11: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ String sPass1 = "111111111";
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "SubStream1", "MediaType1", true, pBytes1, sPass1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempStorage, "BigSubStream1", "MediaType1", true, pBigBytes, sPass1 ) )
+ return false;
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ String sPass2 = "2222222222";
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempSubStorage, "SubStream2", "MediaType2", true, pBytes2, sPass2 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToEncrSubstream( xTempSubStorage, "BigSubStream2", "MediaType2", true, pBigBytes, sPass2 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+
+ // check cloning at current state
+
+
+ // the new storage still was not committed so the clone must be empty
+ XStorage xClonedSubStorage = m_aTestHelper.cloneSubStorage( m_xStorageFactory, xTempStorage, "SubStorage1" );
+
+ if ( xClonedSubStorage == null )
+ {
+ m_aTestHelper.Error( "The result of clone is empty!" );
+ return false;
+ }
+
+ XNameAccess xClonedNameAccess = (XNameAccess) UnoRuntime.queryInterface( XNameAccess.class, xClonedSubStorage );
+ if ( xClonedNameAccess == null )
+ {
+ m_aTestHelper.Error( "XNameAccess is not implemented by the clone!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xClonedSubStorage, "", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( xClonedNameAccess.hasElements() )
+ {
+ m_aTestHelper.Error( "The new substorage still was not committed so it must be empty!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.disposeStorage( xClonedSubStorage ) )
+ return false;
+
+ xClonedSubStorage = null;
+ xClonedNameAccess = null;
+
+ // the new stream was opened, written and closed, that means flashed
+ // so the clone must contain all the information
+ XStream xClonedSubStream = m_aTestHelper.cloneEncrSubStream( xTempStorage, "SubStream1", sPass1 );
+ if ( !m_aTestHelper.InternalCheckStream( xClonedSubStream, "SubStream1", "MediaType1", true, pBytes1, true ) )
+ return false;
+
+ XStream xClonedBigSubStream = m_aTestHelper.cloneEncrSubStream( xTempStorage, "BigSubStream1", sPass1 );
+ if ( !m_aTestHelper.InternalCheckStream( xClonedBigSubStream, "BigSubStream1", "MediaType1", true, pBigBytes, true ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStream( xClonedSubStream, "SubStream1" ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStream( xClonedBigSubStream, "BigSubStream1" ) )
+ return false;
+
+
+ // commit substorage and check cloning
+
+
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ xClonedSubStorage = m_aTestHelper.cloneSubStorage( m_xStorageFactory, xTempStorage, "SubStorage1" );
+ if ( xClonedSubStorage == null )
+ {
+ m_aTestHelper.Error( "The result of clone is empty!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xClonedSubStorage, "MediaType4", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xClonedSubStorage, "SubStream2", "MediaType2", pBytes2, sPass2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xClonedSubStorage, "BigSubStream2", "MediaType2", pBigBytes, sPass2 ) )
+ return false;
+
+
+ // commit the root storage and check cloning
+
+
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ XStorage xCloneOfRoot = m_aTestHelper.cloneStorage( m_xStorageFactory, xTempStorage );
+ if ( xCloneOfRoot == null )
+ {
+ m_aTestHelper.Error( "The result of root clone is empty!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xCloneOfRoot, "MediaType3", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xCloneOfRoot, "SubStream1", "MediaType1", pBytes1, sPass1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xCloneOfRoot, "BigSubStream1", "MediaType1", pBigBytes, sPass1 ) )
+ return false;
+
+ XStorage xSubStorageOfClone = xCloneOfRoot.openStorageElement( "SubStorage1", ElementModes.READ );
+ if ( xSubStorageOfClone == null )
+ {
+ m_aTestHelper.Error( "The result of root clone is wrong!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xSubStorageOfClone, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xSubStorageOfClone, "SubStream2", "MediaType2", pBytes2, sPass2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStream( xSubStorageOfClone, "BigSubStream2", "MediaType2", pBigBytes, sPass2 ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+}
+
diff --git a/package/qa/storages/Test12.java b/package/qa/storages/Test12.java
new file mode 100644
index 0000000000..13850daf47
--- /dev/null
+++ b/package/qa/storages/Test12.java
@@ -0,0 +1,258 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test12 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test12( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test12: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType2",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType3",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose substorage
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+
+
+ // check substorage
+
+
+ if ( !checkSubStorages( xTempStorage, pBytes1, pBigBytes ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+ // now check all the written information with readwrite access
+
+
+ Object oResWriteStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResWriteStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResWriteStorage );
+ if ( xResWriteStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open storage based on input stream!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResWriteStorage, "MediaType2", true, ElementModes.WRITE ) )
+ return false;
+
+ if( !checkSubStorages( xResWriteStorage, pBytes1, pBigBytes ) )
+ return false;
+
+ // try to open for writing after opening for reading
+ XStorage xResWSubStorage = m_aTestHelper.openSubStorage( xResWriteStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xResWSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open substorage for writing after it was opened for reading!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResWSubStorage, "MediaType3", false, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResWSubStorage, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResWSubStorage, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xResWriteStorage ) )
+ return false;
+
+
+
+ // now check all the written information with readonly access
+
+
+ // close the output part of the temporary stream
+ // the output part must present since we already wrote to the stream
+ if ( !m_aTestHelper.closeOutput( xTempFileStream ) )
+ return false;
+
+ XInputStream xTempInStream = m_aTestHelper.getInputStream( xTempFileStream );
+ if ( xTempInStream == null )
+ return false;
+
+ // open input stream
+ // since no mode is provided the result storage must be opened readonly
+ Object pOneArg[] = new Object[1];
+ pOneArg[0] = (Object) xTempInStream;
+
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pOneArg );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open storage based on input stream!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType2", true, ElementModes.READ ) )
+ return false;
+
+ if( !checkSubStorages( xResultStorage, pBytes1, pBigBytes ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+ private boolean checkSubStorages( XStorage xStorage, byte[] pBytes1, byte[] pBigBytes )
+ {
+ XStorage xReadSubStorage1 = m_aTestHelper.openSubStorage( xStorage,
+ "SubStorage1",
+ ElementModes.READ );
+
+ XStorage xReadSubStorage2 = m_aTestHelper.openSubStorage( xStorage,
+ "SubStorage1",
+ ElementModes.READ );
+
+ if ( xReadSubStorage1 == null || xReadSubStorage2 == null )
+ {
+ m_aTestHelper.Error( "Can't open substorage for reading!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xReadSubStorage1, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStorageProperties( xReadSubStorage2, "MediaType3", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xReadSubStorage1, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xReadSubStorage1, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xReadSubStorage2, "SubStream1", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xReadSubStorage2, "BigSubStream1", "MediaType1", true, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStorage( xReadSubStorage1 ) )
+ return false;
+
+ if ( !m_aTestHelper.disposeStorage( xReadSubStorage2 ) )
+ return false;
+
+ return true;
+ }
+}
+
diff --git a/package/qa/storages/Test13.java b/package/qa/storages/Test13.java
new file mode 100644
index 0000000000..649f9d88b4
--- /dev/null
+++ b/package/qa/storages/Test13.java
@@ -0,0 +1,233 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test13 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test13( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test13: " );
+ }
+
+ public boolean test()
+ {
+ String aStreamPrefix = "";
+ for ( int nInd = 0; nInd < 4; ++nInd, aStreamPrefix += "SubStorage" + nInd )
+ if ( !testForPath( aStreamPrefix ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean testForPath( String aStreamPrefix )
+ {
+ try
+ {
+ String aSubStream1Path = aStreamPrefix + "SubStream1";
+ String aSubStream2Path = aStreamPrefix + "SubStream2";
+ String aSubStream3Path = aStreamPrefix + "SubStream3";
+ String aBigSubStream1Path = aStreamPrefix + "BigSubStream1";
+ String aBigSubStream2Path = aStreamPrefix + "BigSubStream2";
+ String aBigSubStream3Path = aStreamPrefix + "BigSubStream3";
+
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ byte pBigBytes[] = new byte[33000];
+ for ( int nInd = 0; nInd < 33000; nInd++ )
+ pBigBytes[nInd] = (byte)( nInd % 128 );
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aSubStream1Path, "MediaType1", true, pBytes1, true ) )
+ return false;
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aBigSubStream1Path, "MediaType1", true, pBigBytes, true ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aSubStream2Path, "MediaType2", false, pBytes2, true ) )
+ return false;
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aBigSubStream2Path, "MediaType2", false, pBigBytes, true ) )
+ return false;
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and don't commit
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aSubStream3Path, "MediaType2", false, pBytes2, false ) )
+ return false;
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aBigSubStream3Path, "MediaType2", false, pBigBytes, false ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempFileStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now reopen the storage,
+ // check all the written and copied information
+ // and change it
+
+
+ // the temporary file must not be locked any more after storage disposing
+ oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempFileStorage, "MediaType3", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStreamH( xTempFileStorage, aSubStream1Path, "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStreamH( xTempFileStorage, aBigSubStream1Path, "MediaType1", true, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStreamH( xTempFileStorage, aSubStream2Path, "MediaType2", false, pBytes2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStreamH( xTempFileStorage, aBigSubStream2Path, "MediaType2", false, pBigBytes ) )
+ return false;
+
+ if ( !m_aTestHelper.cantOpenStreamH( xTempFileStorage, aSubStream3Path, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.cantOpenStreamH( xTempFileStorage, aBigSubStream3Path, ElementModes.READ ) )
+ return false;
+
+ // open existing substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aSubStream1Path, "MediaType3", true, pBytes2, true ) )
+ return false;
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aBigSubStream1Path, "MediaType3", true, pBigBytes, true ) )
+ return false;
+
+
+ // open existing substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and don't commit
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aSubStream2Path, "MediaType3", true, pBytes1, false ) )
+ return false;
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aBigSubStream2Path, "MediaType3", true, pBigBytes, false ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now reopen the storage,
+ // check all the written information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType3", true, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStreamH( xResultStorage, aSubStream1Path, "MediaType3", true, pBytes2 ) )
+ return false;
+ if ( !m_aTestHelper.checkStreamH( xResultStorage, aBigSubStream1Path, "MediaType3", true, pBigBytes ) )
+ return false;
+
+ // the following stream was not committed last time, so the last change must be lost
+ if ( !m_aTestHelper.checkStreamH( xResultStorage, aBigSubStream2Path, "MediaType2", false, pBigBytes ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test14.java b/package/qa/storages/Test14.java
new file mode 100644
index 0000000000..04d4a87874
--- /dev/null
+++ b/package/qa/storages/Test14.java
@@ -0,0 +1,206 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test14 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test14( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test14: " );
+ }
+
+ public boolean test()
+ {
+ String aStreamPrefix = "";
+ for ( int nInd = 0; nInd < 4; ++nInd, aStreamPrefix += "SubStorage" + nInd )
+ if ( !testForPath( aStreamPrefix ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean testForPath( String aStreamPrefix )
+ {
+ try
+ {
+ String aSubStream1Path = aStreamPrefix + "SubStream1";
+ String aSubStream2Path = aStreamPrefix + "SubStream2";
+ String aSubStream3Path = aStreamPrefix + "SubStream3";
+
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+ String sPass1 = "12345";
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WriteBytesToEncrStreamH( xTempFileStorage, aSubStream1Path, "MediaType1", true, pBytes1, sPass1, true ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+ String sPass2 = "54321";
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WriteBytesToEncrStreamH( xTempFileStorage, aSubStream2Path, "MediaType2", false, pBytes2, sPass2, true ) )
+ return false;
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and don't commit
+ if ( !m_aTestHelper.WriteBytesToEncrStreamH( xTempFileStorage, aSubStream3Path, "MediaType2", false, pBytes2, sPass2, false ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempFileStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now reopen the storage,
+ // check all the written and copied information
+ // and change it
+
+
+ // the temporary file must not be locked any more after storage disposing
+ oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempFileStorage, "MediaType3", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStreamH( xTempFileStorage, aSubStream1Path, "MediaType1", pBytes1, sPass1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStreamH( xTempFileStorage, aSubStream2Path, "MediaType2", pBytes2, sPass2 ) )
+ return false;
+
+ if ( !m_aTestHelper.cantOpenEncrStreamH( xTempFileStorage, aSubStream3Path, ElementModes.READ, sPass2 ) )
+ return false;
+
+ // open existing substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WriteBytesToEncrStreamH( xTempFileStorage, aSubStream1Path, "MediaType3", true, pBytes2, sPass1, true ) )
+ return false;
+
+ // open existing substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and don't commit
+ if ( !m_aTestHelper.WriteBytesToEncrStreamH( xTempFileStorage, aSubStream2Path, "MediaType3", true, pBytes1, sPass2, false ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now reopen the storage,
+ // check all the written information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType3", true, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStreamH( xResultStorage, aSubStream1Path, "MediaType3", pBytes2, sPass1 ) )
+ return false;
+
+ // the following stream was not committed last time, so the last change must be lost
+ if ( !m_aTestHelper.checkEncrStreamH( xResultStorage, aSubStream2Path, "MediaType2", pBytes2, sPass2 ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test15.java b/package/qa/storages/Test15.java
new file mode 100644
index 0000000000..a5ac5d0ac7
--- /dev/null
+++ b/package/qa/storages/Test15.java
@@ -0,0 +1,286 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test15 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test15( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test15: " );
+ }
+
+ public boolean test()
+ {
+ String aStreamPrefix = "";
+ for ( int nInd = 0; nInd < 4; ++nInd, aStreamPrefix += "SubStorage" + nInd )
+ if ( !testForPath( aStreamPrefix ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean testForPath( String aStreamPrefix )
+ {
+ try
+ {
+ String aSubStream1Path = aStreamPrefix + "SubStream1";
+ String aSubStream2Path = aStreamPrefix + "SubStream2";
+ String aSubStream3Path = aStreamPrefix + "SubStream3";
+ String aSubStream4Path = aStreamPrefix + "SubStream4";
+
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // set the global password for the root storage
+ XEncryptionProtectedSource xTempStorageEncryption =
+ (XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xTempFileStorage );
+
+ if ( xTempStorageEncryption == null )
+ {
+ m_aTestHelper.Message( "Optional interface XEncryptionProtectedSource is not implemented, feature can not be tested!" );
+ return true;
+ }
+
+ String sPass1 = "12345";
+ String sPass2 = "54321";
+
+ try {
+ xTempStorageEncryption.setEncryptionPassword( sPass1 );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can't set a common encryption key for the storage, exception:" + e );
+ return false;
+ }
+
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WBToSubstrOfEncrH( xTempFileStorage, aSubStream1Path, "MediaType1", true, pBytes1, true, true ) )
+ return false;
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WriteBytesToEncrStreamH( xTempFileStorage, aSubStream2Path, "MediaType2", false, pBytes2, sPass2, true ) )
+ return false;
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WriteBytesToEncrStreamH( xTempFileStorage, aSubStream3Path, "MediaType3", false, pBytes2, sPass2, true ) )
+ return false;
+
+ // open a new substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and don't commit
+ if ( !m_aTestHelper.WBToSubstrOfEncrH( xTempFileStorage, aSubStream4Path, "MediaType2", true, pBytes1, true, false ) )
+ return false;
+
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempFileStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now reopen the storage,
+ // check all the written and copied information
+ // and change it
+
+
+ // the temporary file must not be locked any more after storage disposing
+ oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // set the global password for the root storage
+ xTempStorageEncryption =
+ (XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xTempFileStorage );
+
+ if ( xTempStorageEncryption == null )
+ {
+ m_aTestHelper.Error( "XEncryptionProtectedSource is supported, but can not be retrieved!" );
+ return false;
+ }
+
+ try {
+ xTempStorageEncryption.setEncryptionPassword( sPass2 );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can't set a common encryption key for the storage, exception:" + e );
+ return false;
+ }
+
+
+ if ( !m_aTestHelper.checkStorageProperties( xTempFileStorage, "MediaType3", true, ElementModes.WRITE ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStreamH( xTempFileStorage, aSubStream1Path, "MediaType1", pBytes1, sPass1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStreamH( xTempFileStorage, aSubStream2Path, "MediaType2", true, pBytes2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStreamH( xTempFileStorage, aSubStream3Path, "MediaType3", true, pBytes2 ) )
+ return false;
+
+ if ( !m_aTestHelper.cantOpenEncrStreamH( xTempFileStorage, aSubStream4Path, ElementModes.READ, sPass1 ) )
+ return false;
+
+ // open existing substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and commit
+ if ( !m_aTestHelper.WriteBytesToEncrStreamH( xTempFileStorage, aSubStream1Path, "MediaType4", true, pBytes2, sPass1, true ) )
+ return false;
+
+ // open existing substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and don't commit
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aSubStream2Path, "MediaType5", true, pBytes1, true ) )
+ return false;
+
+ // change the password of the existing stream
+ if ( m_aTestHelper.ChangeStreamPassH( xTempFileStorage, aSubStream2Path, sPass2, sPass1, true ) != 1 )
+ return false;
+
+ // open existing substream hierarchically, set "MediaType" and "Compressed" properties to it, write some bytes
+ // and don't commit
+ if ( !m_aTestHelper.WriteBytesToStreamH( xTempFileStorage, aSubStream3Path, "MediaType5", true, pBytes1, false ) )
+ return false;
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now reopen the storage,
+ // check all the written information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.READ );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ // set the global password for the root storage
+ xTempStorageEncryption =
+ (XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xResultStorage );
+
+ if ( xTempStorageEncryption == null )
+ {
+ m_aTestHelper.Error( "XEncryptionProtectedSource is supported, but can not be retrieved!" );
+ return false;
+ }
+
+ try {
+ xTempStorageEncryption.setEncryptionPassword( sPass1 );
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Can't set a common encryption key for the storage, exception:" + e );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType3", true, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStreamH( xResultStorage, aSubStream1Path, "MediaType4", true, pBytes2 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStreamH( xResultStorage, aSubStream2Path, "MediaType5", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkEncrStreamH( xResultStorage, aSubStream3Path, "MediaType3", pBytes2, sPass2 ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test16.java b/package/qa/storages/Test16.java
new file mode 100644
index 0000000000..36e945e74d
--- /dev/null
+++ b/package/qa/storages/Test16.java
@@ -0,0 +1,177 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test16 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test16( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test16: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage\u0442\u0435\u0441\u04421",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream\u0442\u0435\u0441\u04421", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ byte pBytes2[] = { 2, 2, 2, 2, 2 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, "SubStream\u0442\u0435\u0441\u04422", "MediaType2", false, pBytes2 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // copy xTempStorage to xTempFileStorage
+ // xTempFileStorage will be automatically committed
+ if ( !m_aTestHelper.copyStorage( xTempStorage, xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) || !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now check all the written and copied information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType3", true, ElementModes.WRITE ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage\u0442\u0435\u0441\u04421",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream\u0442\u0435\u0441\u04421", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream\u0442\u0435\u0441\u04422", "MediaType2", false, pBytes2 ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test17.java b/package/qa/storages/Test17.java
new file mode 100644
index 0000000000..b81fc240a7
--- /dev/null
+++ b/package/qa/storages/Test17.java
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.io.XStream;
+import com.sun.star.io.XInputStream;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test17 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test17( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test17: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ XStream xTempFileStream = m_aTestHelper.CreateTempFileStream( m_xMSF );
+ if ( xTempFileStream == null )
+ return false;
+
+ // create storage based on the temporary stream
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) xTempFileStream;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+ String pNames[] = { "SubStream1", "SubStream2", "SubStream3", "SubStream4", "SubStream5", "SubStream6", "SubStream7" };
+
+ for ( int nInd = 0; nInd < pNames.length; nInd++ )
+ {
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstream( xTempSubStorage, pNames[nInd], "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // commit substorage first
+ if ( !m_aTestHelper.commitStorage( xTempSubStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempSubStorage ) )
+ return false;
+ }
+
+ // commit the root storage so the contents must be stored now
+ if ( !m_aTestHelper.commitStorage( xTempStorage ) )
+ return false;
+
+ // dispose used storage to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) )
+ return false;
+
+
+
+ // now check all the written information
+
+
+ // close the output part of the temporary stream
+ // the output part must present since we already wrote to the stream
+ if ( !m_aTestHelper.closeOutput( xTempFileStream ) )
+ return false;
+
+ XInputStream xTempInStream = m_aTestHelper.getInputStream( xTempFileStream );
+ if ( xTempInStream == null )
+ return false;
+
+
+ // open input stream
+ // since no mode is provided the result storage must be opened readonly
+ Object pOneArg[] = new Object[1];
+ pOneArg[0] = (Object) xTempInStream;
+
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pOneArg );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open storage based on input stream!" );
+ return false;
+ }
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ for ( int nInd = 0; nInd < pNames.length; nInd++ )
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, pNames[nInd], "MediaType1", true, pBytes1 ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/Test18.java b/package/qa/storages/Test18.java
new file mode 100644
index 0000000000..3e0f246d1d
--- /dev/null
+++ b/package/qa/storages/Test18.java
@@ -0,0 +1,190 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.XInterface;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XSingleServiceFactory;
+
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import com.sun.star.embed.*;
+
+import share.LogWriter;
+import complex.storages.TestHelper;
+import complex.storages.StorageTest;
+
+public class Test18 implements StorageTest {
+
+ XMultiServiceFactory m_xMSF;
+ XSingleServiceFactory m_xStorageFactory;
+ TestHelper m_aTestHelper;
+
+ public Test18( XMultiServiceFactory xMSF, XSingleServiceFactory xStorageFactory, LogWriter aLogWriter )
+ {
+ m_xMSF = xMSF;
+ m_xStorageFactory = xStorageFactory;
+ m_aTestHelper = new TestHelper( aLogWriter, "Test18: " );
+ }
+
+ public boolean test()
+ {
+ try
+ {
+ // test the default value of Compressed property
+ String sTempFileURL = m_aTestHelper.CreateTempFile( m_xMSF );
+ if ( sTempFileURL == null || sTempFileURL == "" )
+ {
+ m_aTestHelper.Error( "No valid temporary file was created!" );
+ return false;
+ }
+
+ // create temporary storage based on arbitrary medium
+ // after such a storage is closed it is lost
+ Object oTempStorage = m_xStorageFactory.createInstance();
+ XStorage xTempStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xTempStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create temporary storage representation!" );
+ return false;
+ }
+
+ // open a new substorage
+ XStorage xTempSubStorage = m_aTestHelper.openSubStorage( xTempStorage,
+ "SubStorage1",
+ ElementModes.WRITE );
+ if ( xTempSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create substorage!" );
+ return false;
+ }
+
+ byte pBytes1[] = { 1, 1, 1, 1, 1 };
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstreamDefaultCompressed( xTempSubStorage, "SubStream1", "image/jpeg", pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstreamDefaultCompressed( xTempSubStorage, "SubStream2", "image/png", pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstreamDefaultCompressed( xTempSubStorage, "SubStream3", "image/gif", pBytes1 ) )
+ return false;
+
+ // open a new substream, set "MediaType" and "Compressed" properties to it and write some bytes
+ if ( !m_aTestHelper.WriteBytesToSubstreamDefaultCompressed( xTempSubStorage, "SubStream4", "MediaType1", pBytes1 ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempStorage,
+ "MediaType3",
+ true,
+ ElementModes.WRITE ) )
+ return false;
+
+ // set "MediaType" property for storages and check that "IsRoot" and "OpenMode" properties are set correctly
+ if ( !m_aTestHelper.setStorageTypeAndCheckProps( xTempSubStorage,
+ "MediaType4",
+ false,
+ ElementModes.WRITE ) )
+ return false;
+
+ // create temporary storage based on a previously created temporary file
+ Object pArgs[] = new Object[2];
+ pArgs[0] = (Object) sTempFileURL;
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+
+ Object oTempFileStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xTempFileStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oTempFileStorage );
+ if ( xTempFileStorage == null )
+ {
+ m_aTestHelper.Error( "Can't create storage based on temporary file!" );
+ return false;
+ }
+
+ // copy xTempStorage to xTempFileStorage
+ // xTempFileStorage will be automatically committed
+ if ( !m_aTestHelper.copyStorage( xTempStorage, xTempFileStorage ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xTempStorage ) || !m_aTestHelper.disposeStorage( xTempFileStorage ) )
+ return false;
+
+
+ // now check all the written and copied information
+
+
+ // the temporary file must not be locked any more after storage disposing
+ pArgs[1] = Integer.valueOf( ElementModes.WRITE );
+ Object oResultStorage = m_xStorageFactory.createInstanceWithArguments( pArgs );
+ XStorage xResultStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oResultStorage );
+ if ( xResultStorage == null )
+ {
+ m_aTestHelper.Error( "Can't reopen storage based on temporary file!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultStorage, "MediaType3", true, ElementModes.WRITE ) )
+ return false;
+
+ // open existing substorage
+ XStorage xResultSubStorage = m_aTestHelper.openSubStorage( xResultStorage,
+ "SubStorage1",
+ ElementModes.READ );
+ if ( xResultSubStorage == null )
+ {
+ m_aTestHelper.Error( "Can't open existing substorage!" );
+ return false;
+ }
+
+ if ( !m_aTestHelper.checkStorageProperties( xResultSubStorage, "MediaType4", false, ElementModes.READ ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream1", "image/jpeg", false, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream2", "image/png", false, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream3", "image/gif", false, pBytes1 ) )
+ return false;
+
+ if ( !m_aTestHelper.checkStream( xResultSubStorage, "SubStream4", "MediaType1", true, pBytes1 ) )
+ return false;
+
+ // dispose used storages to free resources
+ if ( !m_aTestHelper.disposeStorage( xResultStorage ) )
+ return false;
+
+ return true;
+ }
+ catch( Exception e )
+ {
+ m_aTestHelper.Error( "Exception: " + e );
+ return false;
+ }
+ }
+
+}
+
diff --git a/package/qa/storages/TestHelper.java b/package/qa/storages/TestHelper.java
new file mode 100644
index 0000000000..d6a4fd280d
--- /dev/null
+++ b/package/qa/storages/TestHelper.java
@@ -0,0 +1,1679 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.storages;
+
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.uno.AnyConverter;
+
+import com.sun.star.lang.*;
+import com.sun.star.embed.*;
+import com.sun.star.packages.*;
+import com.sun.star.io.*;
+import com.sun.star.beans.*;
+
+import share.LogWriter;
+
+public class TestHelper {
+
+ LogWriter m_aLogWriter;
+ String m_sTestPrefix;
+
+ public TestHelper( LogWriter aLogWriter, String sTestPrefix )
+ {
+ m_aLogWriter = aLogWriter;
+ m_sTestPrefix = sTestPrefix;
+ }
+
+ public boolean WriteBytesToStream( XStream xStream,
+ String sStreamName,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes )
+ {
+ // get output stream of substream
+ XOutputStream xOutput = xStream.getOutputStream();
+ if ( xOutput == null )
+ {
+ Error( "Can't get XOutputStream implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // get XTruncate implementation from output stream
+ XTruncate xTruncate = (XTruncate) UnoRuntime.queryInterface( XTruncate.class, xOutput );
+ if ( xTruncate == null )
+ {
+ Error( "Can't get XTruncate implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // write requested byte sequence
+ try
+ {
+ xTruncate.truncate();
+ xOutput.writeBytes( pBytes );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't write to stream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStream );
+ if ( xPropSet == null )
+ {
+ Error( "Can't get XPropertySet implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // set properties to the stream
+ try
+ {
+ xPropSet.setPropertyValue( "MediaType", sMediaType );
+ xPropSet.setPropertyValue( "Compressed", Boolean.valueOf( bCompressed ) );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't set properties to substream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ // check size property of the stream
+ try
+ {
+ long nSize = AnyConverter.toLong( xPropSet.getPropertyValue( "Size" ) );
+ if ( nSize != pBytes.length )
+ {
+ Error( "The 'Size' property of substream '" + sStreamName + "' contains wrong value!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get 'Size' property from substream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean WriteBytesToSubstreamDefaultCompressed( XStorage xStorage,
+ String sStreamName,
+ String sMediaType,
+ byte[] pBytes )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xStorage.openStreamElement( sStreamName, ElementModes.WRITE );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't create substream '" + sStreamName + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
+ return false;
+ }
+
+ // get output stream of substream
+ XOutputStream xOutput = xSubStream.getOutputStream();
+ if ( xOutput == null )
+ {
+ Error( "Can't get XOutputStream implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // get XTruncate implementation from output stream
+ XTruncate xTruncate = (XTruncate) UnoRuntime.queryInterface( XTruncate.class, xOutput );
+ if ( xTruncate == null )
+ {
+ Error( "Can't get XTruncate implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // write requested byte sequence
+ try
+ {
+ xTruncate.truncate();
+ xOutput.writeBytes( pBytes );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't write to stream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xSubStream );
+ if ( xPropSet == null )
+ {
+ Error( "Can't get XPropertySet implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // set properties to the stream
+ // do not set the compressed property
+ try
+ {
+ xPropSet.setPropertyValue( "MediaType", sMediaType );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't set properties to substream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ // check size property of the stream
+ try
+ {
+ long nSize = AnyConverter.toLong( xPropSet.getPropertyValue( "Size" ) );
+ if ( nSize != pBytes.length )
+ {
+ Error( "The 'Size' property of substream '" + sStreamName + "' contains wrong value!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get 'Size' property from substream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sStreamName ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean WriteBytesToSubstream( XStorage xStorage,
+ String sStreamName,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xStorage.openStreamElement( sStreamName, ElementModes.WRITE );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't create substream '" + sStreamName + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
+ return false;
+ }
+
+ if ( !WriteBytesToStream( xSubStream, sStreamName, sMediaType, bCompressed, pBytes ) )
+ return false;
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sStreamName ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean WriteBytesToEncrSubstream( XStorage xStorage,
+ String sStreamName,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes,
+ String sPass )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xStorage.openEncryptedStreamElement( sStreamName, ElementModes.WRITE, sPass );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't create substream '" + sStreamName + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
+ return false;
+ }
+
+ if ( !WriteBytesToStream( xSubStream, sStreamName, sMediaType, bCompressed, pBytes ) )
+ return false;
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sStreamName ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean WBToSubstrOfEncr( XStorage xStorage,
+ String sStreamName,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes,
+ boolean bEncrypted )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xStorage.openStreamElement( sStreamName, ElementModes.WRITE );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't create substream '" + sStreamName + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
+ return false;
+ }
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xSubStream );
+ if ( xPropSet == null )
+ {
+ Error( "Can't get XPropertySet implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ // set properties to the stream
+ try
+ {
+ xPropSet.setPropertyValue( "UseCommonStoragePasswordEncryption", Boolean.valueOf( bEncrypted ) );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't set 'UseCommonStoragePasswordEncryption' property to substream '" + sStreamName + "', exception: " + e );
+ return false;
+ }
+
+ if ( !WriteBytesToStream( xSubStream, sStreamName, sMediaType, bCompressed, pBytes ) )
+ return false;
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sStreamName ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean WriteBytesToStreamH( XStorage xStorage,
+ String sStreamPath,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes,
+ boolean bCommit )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ XHierarchicalStorageAccess xHStorage =
+ (XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
+ if ( xHStorage == null )
+ {
+ Error( "The storage does not support hierarchical access!" );
+ return false;
+ }
+
+ Object oSubStream = xHStorage.openStreamElementByHierarchicalName( sStreamPath, ElementModes.WRITE );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't create substream '" + sStreamPath + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamPath + "', exception : " + e + "!" );
+ return false;
+ }
+
+ if ( !WriteBytesToStream( xSubStream, sStreamPath, sMediaType, bCompressed, pBytes ) )
+ return false;
+
+ XTransactedObject xTransact =
+ (XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xSubStream );
+ if ( xTransact == null )
+ {
+ Error( "Substream '" + sStreamPath + "', stream opened for writing must be transacted!" );
+ return false;
+ }
+
+ if ( bCommit )
+ {
+ try {
+ xTransact.commit();
+ } catch( Exception e )
+ {
+ Error( "Can't commit storage after substream '" + sStreamPath + "' change, exception : " + e + "!" );
+ return false;
+ }
+ }
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sStreamPath ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean WriteBytesToEncrStreamH( XStorage xStorage,
+ String sStreamPath,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes,
+ String sPass,
+ boolean bCommit )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ XHierarchicalStorageAccess xHStorage =
+ (XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
+ if ( xHStorage == null )
+ {
+ Error( "The storage does not support hierarchical access!" );
+ return false;
+ }
+
+ Object oSubStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sStreamPath,
+ ElementModes.WRITE,
+ sPass );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't create substream '" + sStreamPath + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamPath + "', exception : " + e + "!" );
+ return false;
+ }
+
+ if ( !WriteBytesToStream( xSubStream, sStreamPath, sMediaType, bCompressed, pBytes ) )
+ return false;
+
+ XTransactedObject xTransact =
+ (XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xSubStream );
+ if ( xTransact == null )
+ {
+ Error( "Substream '" + sStreamPath + "', stream opened for writing must be transacted!" );
+ return false;
+ }
+
+ if ( bCommit )
+ {
+ try {
+ xTransact.commit();
+ } catch( Exception e )
+ {
+ Error( "Can't commit storage after substream '" + sStreamPath + "' change, exception : " + e + "!" );
+ return false;
+ }
+ }
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sStreamPath ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean WBToSubstrOfEncrH( XStorage xStorage,
+ String sStreamPath,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes,
+ boolean bEncrypted,
+ boolean bCommit )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ XHierarchicalStorageAccess xHStorage =
+ (XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
+ if ( xHStorage == null )
+ {
+ Error( "The storage does not support hierarchical access!" );
+ return false;
+ }
+
+ Object oSubStream = xHStorage.openStreamElementByHierarchicalName( sStreamPath, ElementModes.WRITE );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't create substream '" + sStreamPath + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamPath + "', exception : " + e + "!" );
+ return false;
+ }
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xSubStream );
+ if ( xPropSet == null )
+ {
+ Error( "Can't get XPropertySet implementation from substream '" + sStreamPath + "'!" );
+ return false;
+ }
+
+ // set properties to the stream
+ try
+ {
+ xPropSet.setPropertyValue( "UseCommonStoragePasswordEncryption", Boolean.valueOf( bEncrypted ) );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't set 'UseCommonStoragePasswordEncryption' property to substream '" + sStreamPath + "', exception: " + e );
+ return false;
+ }
+
+ if ( !WriteBytesToStream( xSubStream, sStreamPath, sMediaType, bCompressed, pBytes ) )
+ return false;
+
+ XTransactedObject xTransact =
+ (XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xSubStream );
+ if ( xTransact == null )
+ {
+ Error( "Substream '" + sStreamPath + "', stream opened for writing must be transacted!" );
+ return false;
+ }
+
+ if ( bCommit )
+ {
+ try {
+ xTransact.commit();
+ } catch( Exception e )
+ {
+ Error( "Can't commit storage after substream '" + sStreamPath + "' change, exception : " + e + "!" );
+ return false;
+ }
+ }
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sStreamPath ) )
+ return false;
+
+ return true;
+ }
+
+ public int ChangeStreamPass( XStorage xStorage,
+ String sStreamName,
+ String sOldPass,
+ String sNewPass )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xStorage.openEncryptedStreamElement( sStreamName, ElementModes.WRITE, sOldPass );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't open substream '" + sStreamName + "'!" );
+ return 0;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open substream '" + sStreamName + "', exception : " + e + "!" );
+ return 0;
+ }
+
+
+ // change the password for the stream
+ XEncryptionProtectedSource xStreamEncryption =
+ (XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xSubStream );
+
+ if ( xStreamEncryption == null )
+ {
+ Message( "Optional interface XEncryptionProtectedSource is not implemented, feature can not be tested!" );
+ return -1;
+ }
+
+ try {
+ xStreamEncryption.setEncryptionPassword( sNewPass );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't change encryption key of the substream '" + sStreamName + "', exception:" + e );
+ return 0;
+ }
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sStreamName ) )
+ return 0;
+
+ return 1;
+ }
+
+ public int ChangeStreamPassH( XStorage xStorage,
+ String sPath,
+ String sOldPass,
+ String sNewPass,
+ boolean bCommit )
+ {
+ // open substream element
+ XHierarchicalStorageAccess xHStorage =
+ (XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
+ if ( xHStorage == null )
+ {
+ Error( "The storage does not support hierarchical access!" );
+ return 0;
+ }
+
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sPath, ElementModes.WRITE, sOldPass );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't open encrypted substream '" + sPath + "'!" );
+ return 0;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open encrypted substream '" + sPath + "', exception : " + e + "!" );
+ return 0;
+ }
+
+ // change the password for the stream
+ XEncryptionProtectedSource xStreamEncryption =
+ (XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xSubStream );
+
+ if ( xStreamEncryption == null )
+ {
+ Message( "Optional interface XEncryptionProtectedSource is not implemented, feature can not be tested!" );
+ return -1;
+ }
+
+ try {
+ xStreamEncryption.setEncryptionPassword( sNewPass );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't change encryption key of the substream '" + sPath + "', exception:" + e );
+ return 0;
+ }
+
+ XTransactedObject xTransact =
+ (XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xSubStream );
+ if ( xTransact == null )
+ {
+ Error( "Substream '" + sPath + "', stream opened for writing must be transacted!" );
+ return 0;
+ }
+
+ if ( bCommit )
+ {
+ try {
+ xTransact.commit();
+ } catch( Exception e )
+ {
+ Error( "Can't commit storage after substream '" + sPath + "' change, exception : " + e + "!" );
+ return 0;
+ }
+ }
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sPath ) )
+ return 0;
+
+ return 1;
+ }
+
+ public boolean setStorageTypeAndCheckProps( XStorage xStorage, String sMediaType, boolean bIsRoot, int nMode )
+ {
+ boolean bOk = false;
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStorage );
+ if ( xPropSet != null )
+ {
+ try
+ {
+ // set "MediaType" property to the stream
+ xPropSet.setPropertyValue( "MediaType", sMediaType );
+
+ // get "IsRoot" and "OpenMode" properties and control there values
+ boolean bPropIsRoot = AnyConverter.toBoolean( xPropSet.getPropertyValue( "IsRoot" ) );
+ int nPropMode = AnyConverter.toInt( xPropSet.getPropertyValue( "OpenMode" ) );
+
+ bOk = true;
+ if ( bPropIsRoot != bIsRoot )
+ {
+ Error( "'IsRoot' property contains wrong value!" );
+ bOk = false;
+ }
+
+ if ( ( bIsRoot
+ && ( nPropMode | ElementModes.READ ) != ( nMode | ElementModes.READ ) )
+ || ( !bIsRoot && ( nPropMode & nMode ) != nMode ) )
+ {
+ Error( "'OpenMode' property contains wrong value, expected " + nMode + ", in reality " + nPropMode + "!" );
+ bOk = false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't control properties of substorage, exception: " + e );
+ }
+ }
+ else
+ {
+ Error( "Can't get XPropertySet implementation from storage!" );
+ }
+
+ return bOk;
+ }
+
+ public boolean checkStorageProperties( XStorage xStorage, String sMediaType, boolean bIsRoot, int nMode )
+ {
+ boolean bOk = false;
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStorage );
+ if ( xPropSet != null )
+ {
+ try
+ {
+ // get "MediaType", "IsRoot" and "OpenMode" properties and control there values
+ String sPropMediaType = AnyConverter.toString( xPropSet.getPropertyValue( "MediaType" ) );
+ boolean bPropIsRoot = AnyConverter.toBoolean( xPropSet.getPropertyValue( "IsRoot" ) );
+ int nPropMode = AnyConverter.toInt( xPropSet.getPropertyValue( "OpenMode" ) );
+
+ bOk = true;
+ if ( !sPropMediaType.equals( sMediaType ) )
+ {
+ Error( "'MediaType' property contains wrong value, expected '"
+ + sMediaType + "', set '" + sPropMediaType + "' !" );
+ bOk = false;
+ }
+
+ if ( bPropIsRoot != bIsRoot )
+ {
+ Error( "'IsRoot' property contains wrong value!" );
+ bOk = false;
+ }
+
+ if ( ( bIsRoot
+ && ( nPropMode | ElementModes.READ ) != ( nMode | ElementModes.READ ) )
+ || ( !bIsRoot && ( nPropMode & nMode ) != nMode ) )
+ {
+ Error( "'OpenMode' property contains wrong value, expected " + nMode + ", in reality " + nPropMode + "!" );
+ bOk = false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get properties of substorage, exception: " + e );
+ }
+ }
+ else
+ {
+ Error( "Can't get XPropertySet implementation from storage!" );
+ }
+
+ return bOk;
+ }
+
+ public boolean InternalCheckStream( XStream xStream,
+ String sName,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes,
+ boolean bCheckCompressed )
+ {
+ // get input stream of substream
+ XInputStream xInput = xStream.getInputStream();
+ if ( xInput == null )
+ {
+ Error( "Can't get XInputStream implementation from substream '" + sName + "'!" );
+ return false;
+ }
+
+ byte pContents[][] = new byte[1][]; // ???
+
+ // read contents
+ try
+ {
+ xInput.readBytes( pContents, pBytes.length + 1 );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't read from stream '" + sName + "', exception: " + e );
+ return false;
+ }
+
+ // check size of stream data
+ if ( pContents.length == 0 )
+ {
+ Error( "SubStream '" + sName + "' reading produced disaster!" );
+ return false;
+ }
+
+ if ( pBytes.length != pContents[0].length )
+ {
+ Error( "SubStream '" + sName + "' contains wrong amount of data! (" + pContents[0].length + "/" + pBytes.length + ")" );
+ return false;
+ }
+
+ // check stream data
+ for ( int ind = 0; ind < pBytes.length; ind++ )
+ {
+ if ( pBytes[ind] != pContents[0][ind] )
+ {
+ Error( "SubStream '" + sName + "' contains wrong data! ( byte num. "
+ + ind + " should be " + pBytes[ind] + " but it is " + pContents[0][ind] + ")" );
+ return false;
+ }
+ }
+
+ // check properties
+ boolean bOk = false;
+
+ // get access to the XPropertySet interface
+ XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStream );
+ if ( xPropSet != null )
+ {
+ try
+ {
+ // get "MediaType" and "Size" properties and control there values
+ String sPropMediaType = AnyConverter.toString( xPropSet.getPropertyValue( "MediaType" ) );
+ long nPropSize = AnyConverter.toLong( xPropSet.getPropertyValue( "Size" ) );
+ boolean bPropCompress = AnyConverter.toBoolean( xPropSet.getPropertyValue( "Compressed" ) );
+
+ bOk = true;
+ if ( !sPropMediaType.equals( sMediaType ) )
+ {
+ Error( "'MediaType' property contains wrong value for stream '" + sName + "',\nexpected: '"
+ + sMediaType + "', set: '" + sPropMediaType + "'!" );
+ bOk = false;
+ }
+
+ if ( nPropSize != pBytes.length )
+ {
+ Error( "'Size' property contains wrong value for stream'" + sName + "'!" );
+ bOk = false;
+ }
+
+ if ( bCheckCompressed && bPropCompress != bCompressed )
+ {
+ Error( "'Compressed' property contains wrong value for stream'" + sName + "'!" );
+ bOk = false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get properties of substream '" + sName + "', exception: " + e );
+ }
+ }
+ else
+ {
+ Error( "Can't get XPropertySet implementation from stream '" + sName + "'!" );
+ }
+
+ return bOk;
+ }
+
+ public boolean checkStream( XStorage xParentStorage,
+ String sName,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes )
+ {
+ // open substream element first
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xParentStorage.openStreamElement( sName, ElementModes.READ );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't open substream '" + sName + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open substream '" + sName + "', exception : " + e + "!" );
+ return false;
+ }
+
+ boolean bResult = InternalCheckStream( xSubStream, sName, sMediaType, bCompressed, pBytes, true );
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sName ) )
+ return false;
+
+ return bResult;
+ }
+
+ public boolean checkEncrStream( XStorage xParentStorage,
+ String sName,
+ String sMediaType,
+ byte[] pBytes,
+ String sPass )
+ {
+ // Important: a common password for any of parent storage should not be set or
+ // should be different from sPass
+
+ try
+ {
+ Object oSubStream = xParentStorage.openStreamElement( sName, ElementModes.READ );
+ Error( "Encrypted stream '" + sName + "' was opened without password!" );
+ return false;
+ }
+ catch( WrongPasswordException wpe )
+ {}
+ catch( Exception e )
+ {
+ Error( "Unexpected exception in case of opening of encrypted stream '" + sName + "' without password: " + e + "!" );
+ return false;
+ }
+
+ String sWrongPass = "11";
+ sWrongPass += sPass;
+ try
+ {
+ Object oSubStream = xParentStorage.openEncryptedStreamElement( sName, ElementModes.READ, sWrongPass );
+ Error( "Encrypted stream '" + sName + "' was opened with wrong password!" );
+ return false;
+ }
+ catch( WrongPasswordException wpe )
+ {}
+ catch( Exception e )
+ {
+ Error( "Unexpected exception in case of opening of encrypted stream '" + sName + "' with wrong password: " + e + "!" );
+ return false;
+ }
+
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xParentStorage.openEncryptedStreamElement( sName, ElementModes.READ, sPass );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't open encrypted substream '" + sName + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open encrypted substream '" + sName + "', exception : " + e + "!" );
+ return false;
+ }
+
+ // encrypted streams will be compressed always, so after the storing this property is always true,
+ // although before the storing it can be set to false ( it is not always clear whether a stream is encrypted
+ // before the storing )
+ boolean bResult = InternalCheckStream( xSubStream, sName, sMediaType, true, pBytes, false );
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sName ) )
+ return false;
+
+ return bResult;
+ }
+
+ public boolean checkStreamH( XStorage xParentStorage,
+ String sPath,
+ String sMediaType,
+ boolean bCompressed,
+ byte[] pBytes )
+ {
+ // open substream element first
+ XStream xSubStream = null;
+ try
+ {
+ XHierarchicalStorageAccess xHStorage =
+ (XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xParentStorage );
+ if ( xHStorage == null )
+ {
+ Error( "The storage does not support hierarchical access!" );
+ return false;
+ }
+
+ Object oSubStream = xHStorage.openStreamElementByHierarchicalName( sPath, ElementModes.READ );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't open substream '" + sPath + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open substream '" + sPath + "', exception : " + e + "!" );
+ return false;
+ }
+
+ boolean bResult = InternalCheckStream( xSubStream, sPath, sMediaType, bCompressed, pBytes, true );
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sPath ) )
+ return false;
+
+ return bResult;
+ }
+
+ public boolean checkEncrStreamH( XStorage xParentStorage,
+ String sPath,
+ String sMediaType,
+ byte[] pBytes,
+ String sPass )
+ {
+ // Important: a common password for any of parent storage should not be set or
+ // should be different from sPass
+ XHierarchicalStorageAccess xHStorage =
+ (XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xParentStorage );
+ if ( xHStorage == null )
+ {
+ Error( "The storage does not support hierarchical access!" );
+ return false;
+ }
+
+ try
+ {
+ Object oSubStream = xHStorage.openStreamElementByHierarchicalName( sPath, ElementModes.READ );
+ XStream xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ Error( "Encrypted substream '" + sPath + "' was opened without password!" );
+ return false;
+ }
+ catch( WrongPasswordException wpe )
+ {}
+ catch( Exception e )
+ {
+ Error( "Unexpected exception in case of opening of encrypted stream '" + sPath + "' without password: " + e + "!" );
+ return false;
+ }
+
+ String sWrongPass = "11";
+ sWrongPass += sPass;
+ try
+ {
+ Object oSubStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sPath, ElementModes.READ, sWrongPass );
+ XStream xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ Error( "Encrypted substream '" + sPath + "' was opened with wrong password!" );
+ return false;
+ }
+ catch( WrongPasswordException wpe )
+ {}
+ catch( Exception e )
+ {
+ Error( "Unexpected exception in case of opening of encrypted stream '" + sPath + "' with wrong password: " + e + "!" );
+ return false;
+ }
+
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sPath, ElementModes.READ, sPass );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ {
+ Error( "Can't open encrypted substream '" + sPath + "'!" );
+ return false;
+ }
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open encrypted substream '" + sPath + "', exception : " + e + "!" );
+ return false;
+ }
+
+ // encrypted streams will be compressed always, so after the storing this property is always true,
+ // although before the storing it can be set to false ( it is not always clear whether a stream is encrypted
+ // before the storing )
+ boolean bResult = InternalCheckStream( xSubStream, sPath, sMediaType, true, pBytes, false );
+
+ // free the stream resources, garbage collector may remove the object too late
+ if ( !disposeStream( xSubStream, sPath ) )
+ return false;
+
+ return bResult;
+ }
+
+ public boolean copyStorage( XStorage xSourceStorage, XStorage xDestStorage )
+ {
+ // copy xSourceStorage to xDestStorage
+ try
+ {
+ xSourceStorage.copyToStorage( xDestStorage );
+ }
+ catch( Exception e )
+ {
+ Error( "Storage copying failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean commitStorage( XStorage xStorage )
+ {
+ // XTransactedObject must be supported by storages
+ XTransactedObject xTransact = (XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xStorage );
+ if ( xTransact == null )
+ {
+ Error( "Storage doesn't implement transacted access!" );
+ return false;
+ }
+
+ try
+ {
+ xTransact.commit();
+ }
+ catch( Exception e )
+ {
+ Error( "Storage commit failed, exception:" + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean disposeStream( XStream xStream, String sStreamName )
+ {
+ XComponent xComponent = (XComponent) UnoRuntime.queryInterface( XComponent.class, xStream );
+ if ( xComponent == null )
+ {
+ Error( "Can't get XComponent implementation from substream '" + sStreamName + "'!" );
+ return false;
+ }
+
+ try
+ {
+ xComponent.dispose();
+ }
+ catch( Exception e )
+ {
+ Error( "Substream '" + sStreamName + "' disposing throws exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean disposeStorage( XStorage xStorage )
+ {
+ // dispose the storage
+ XComponent xComponent = (XComponent) UnoRuntime.queryInterface( XComponent.class, xStorage );
+ if ( xComponent == null )
+ {
+ Error( "Can't retrieve XComponent implementation from storage!" );
+ return false;
+ }
+
+ try
+ {
+ xComponent.dispose();
+ }
+ catch( Exception e )
+ {
+ Error( "Storage disposing failed!" );
+ return false;
+ }
+
+ return true;
+ }
+
+ public XInputStream getInputStream( XStream xStream )
+ {
+ XInputStream xInTemp = null;
+ try
+ {
+ xInTemp = xStream.getInputStream();
+ if ( xInTemp == null )
+ Error( "Can't get the input part of a stream!" );
+ }
+ catch ( Exception e )
+ {
+ Error( "Can't get the input part of a stream, exception :" + e );
+ }
+
+ return xInTemp;
+ }
+
+ public boolean closeOutput( XStream xStream )
+ {
+ XOutputStream xOutTemp = null;
+ try
+ {
+ xOutTemp = xStream.getOutputStream();
+ if ( xOutTemp == null )
+ {
+ Error( "Can't get the output part of a stream!" );
+ return false;
+ }
+ }
+ catch ( Exception e )
+ {
+ Error( "Can't get the output part of a stream, exception :" + e );
+ return false;
+ }
+
+ try
+ {
+ xOutTemp.closeOutput();
+ }
+ catch ( Exception e )
+ {
+ Error( "Can't close output part of a stream, exception :" + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public XStorage openSubStorage( XStorage xStorage, String sName, int nMode )
+ {
+ // open existing substorage
+ try
+ {
+ Object oSubStorage = xStorage.openStorageElement( sName, nMode );
+ XStorage xSubStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oSubStorage );
+ return xSubStorage;
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open substorage '" + sName + "', exception: " + e );
+ }
+
+ return null;
+ }
+
+ public XStream CreateTempFileStream( XMultiServiceFactory xMSF )
+ {
+ // try to get temporary file representation
+ XStream xTempFileStream = null;
+ try
+ {
+ Object oTempFile = xMSF.createInstance( "com.sun.star.io.TempFile" );
+ xTempFileStream = (XStream)UnoRuntime.queryInterface( XStream.class, oTempFile );
+ }
+ catch( Exception e )
+ {}
+
+ if ( xTempFileStream == null )
+ Error( "Can't create temporary file!" );
+
+ return xTempFileStream;
+ }
+
+ public String CreateTempFile( XMultiServiceFactory xMSF )
+ {
+ String sResult = null;
+
+ // try to get temporary file representation
+ XPropertySet xTempFileProps = null;
+ try
+ {
+ Object oTempFile = xMSF.createInstance( "com.sun.star.io.TempFile" );
+ xTempFileProps = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, oTempFile );
+ }
+ catch( Exception e )
+ {}
+
+ if ( xTempFileProps != null )
+ {
+ try
+ {
+ xTempFileProps.setPropertyValue( "RemoveFile", Boolean.FALSE );
+ sResult = AnyConverter.toString( xTempFileProps.getPropertyValue( "Uri" ) );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't control TempFile properties, exception: " + e );
+ }
+ }
+ else
+ {
+ Error( "Can't create temporary file representation!" );
+ }
+
+ // close temporary file explicitly
+ try
+ {
+ XStream xStream = (XStream)UnoRuntime.queryInterface( XStream.class, xTempFileProps );
+ if ( xStream != null )
+ {
+ XOutputStream xOut = xStream.getOutputStream();
+ if ( xOut != null )
+ xOut.closeOutput();
+
+ XInputStream xIn = xStream.getInputStream();
+ if ( xIn != null )
+ xIn.closeInput();
+ }
+ else
+ Error( "Can't close TempFile!" );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't close TempFile, exception: " + e );
+ }
+
+ return sResult;
+ }
+
+ public boolean copyElementTo( XStorage xSource, String sName, XStorage xDest )
+ {
+ // copy element with name sName from xSource to xDest
+ try
+ {
+ xSource.copyElementTo( sName, xDest, sName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element copying failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean copyElementTo( XStorage xSource, String sName, XStorage xDest, String sTargetName )
+ {
+ // copy element with name sName from xSource to xDest
+ try
+ {
+ xSource.copyElementTo( sName, xDest, sTargetName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element copying failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean moveElementTo( XStorage xSource, String sName, XStorage xDest )
+ {
+ // move element with name sName from xSource to xDest
+ try
+ {
+ xSource.moveElementTo( sName, xDest, sName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element moving failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean renameElement( XStorage xStorage, String sOldName, String sNewName )
+ {
+ // rename element with name sOldName to sNewName
+ try
+ {
+ xStorage.renameElement( sOldName, sNewName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element renaming failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean removeElement( XStorage xStorage, String sName )
+ {
+ // remove element with name sName
+ try
+ {
+ xStorage.removeElement( sName );
+ }
+ catch( Exception e )
+ {
+ Error( "Element removing failed, exception: " + e );
+ return false;
+ }
+
+ return true;
+ }
+
+ public XStream OpenStream( XStorage xStorage,
+ String sStreamName,
+ int nMode )
+ {
+ // open substream element
+ XStream xSubStream = null;
+ try
+ {
+ Object oSubStream = xStorage.openStreamElement( sStreamName, nMode );
+ xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
+ if ( xSubStream == null )
+ Error( "Can't create substream '" + sStreamName + "'!" );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
+ }
+
+ return xSubStream;
+ }
+
+ public boolean compareRawMethodsOnEncrStream( XStorage xStorage, String sStreamName )
+ {
+
+ XStorageRawAccess xRawStorage;
+ try
+ {
+ xRawStorage = (XStorageRawAccess) UnoRuntime.queryInterface( XStorageRawAccess.class, xStorage );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't get raw access to the storage, exception : " + e + "!" );
+ return false;
+ }
+
+ if ( xRawStorage == null )
+ {
+ Error( "Can't get raw access to the storage!" );
+ return false;
+ }
+
+ XInputStream xHeadRawStream = null;
+ try
+ {
+ xHeadRawStream = xRawStorage.getRawEncrStreamElement( sStreamName );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open encrypted stream '" + sStreamName + "' in raw mode with header, exception : " + e + "!" );
+ }
+
+ XInputStream xPlainRawStream = null;
+ try
+ {
+ xPlainRawStream = xRawStorage.getPlainRawStreamElement( sStreamName );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't open encrypted stream '" + sStreamName + "' in raw mode with header, exception : " + e + "!" );
+ }
+
+ if ( xHeadRawStream == null || xPlainRawStream == null )
+ {
+ Error( "Can't open encrypted stream '" + sStreamName + "' in raw modes!" );
+ return false;
+ }
+
+ try
+ {
+ byte pData[][] = new byte[1][38];
+ if ( xHeadRawStream.readBytes( pData, 38 ) != 38 )
+ {
+ Error( "Can't read header of encrypted stream '" + sStreamName + "' raw representations!" );
+ return false;
+ }
+
+ if ( pData[0][0] != 0x4d || pData[0][1] != 0x4d || pData[0][2] != 0x02 || pData[0][3] != 0x05 )
+ {
+ Error( "No signature in the header of encrypted stream '" + sStreamName + "' raw representations!" );
+ return false;
+ }
+
+ int nVariableHeaderLength =
+ ( pData[0][30] + pData[0][31] * 0x100 ) // salt length
+ + ( pData[0][32] + pData[0][33] * 0x100 ) // iv length
+ + ( pData[0][34] + pData[0][35] * 0x100 ) // digest length
+ + ( pData[0][36] + pData[0][37] * 0x100 ); // mediatype length
+
+ xHeadRawStream.skipBytes( nVariableHeaderLength );
+
+ byte pRawData1[][] = new byte[1][32000];
+ byte pRawData2[][] = new byte[1][32000];
+ int nRead1 = 0;
+ int nRead2 = 0;
+
+ do
+ {
+ nRead1 = xHeadRawStream.readBytes( pRawData1, 32000 );
+ nRead2 = xPlainRawStream.readBytes( pRawData2, 32000 );
+
+ if ( nRead1 != nRead2 )
+ {
+ Error( "The encrypted stream '" + sStreamName + "' raw representations have different size! nRead1 - nRead2 = " + ( Integer.valueOf( nRead1 - nRead2 ) ).toString() );
+ return false;
+ }
+
+ for ( int nInd = 0; nInd < nRead1; nInd++ )
+ if ( pRawData1[0][nInd] != pRawData2[0][nInd] )
+ {
+ Error( "The encrypted stream '" + sStreamName + "' raw representations have different data!" );
+ return false;
+ }
+ }
+ while( nRead1 == 32000 );
+ }
+ catch ( Exception e )
+ {
+ Error( "Can't compare stream '" + sStreamName + "' raw representations, exception : " + e + "!" );
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean cantOpenStorage( XStorage xStorage, String sName )
+ {
+ // try to open an opened substorage, open call must fail
+ try
+ {
+ Object oDummyStorage = xStorage.openStorageElement( sName, ElementModes.READ );
+ Error( "The trying to reopen opened substorage '" + sName + "' must fail!" );
+ }
+ catch( Exception e )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean cantOpenStream( XStorage xStorage, String sName, int nMode )
+ {
+ // try to open the substream with specified mode must fail
+ try
+ {
+ Object oDummyStream = xStorage.openStreamElement( sName, nMode );
+ Error( "The trying to open substream '" + sName + "' must fail!" );
+ }
+ catch( Exception e )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean cantOpenStreamH( XStorage xStorage, String sPath, int nMode )
+ {
+ // try to open the substream with specified mode must fail
+
+ XHierarchicalStorageAccess xHStorage =
+ (XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
+ if ( xHStorage == null )
+ {
+ Error( "The storage does not support hierarchical access!" );
+ return false;
+ }
+
+ try
+ {
+ Object oDummyStream = xHStorage.openStreamElementByHierarchicalName( sPath, nMode );
+ Error( "The trying to open substream '" + sPath + "' must fail!" );
+ }
+ catch( Exception e )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean cantOpenEncrStreamH( XStorage xStorage, String sPath, int nMode, String aPass )
+ {
+ // try to open the substream with specified mode must fail
+
+ XHierarchicalStorageAccess xHStorage =
+ (XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
+ if ( xHStorage == null )
+ {
+ Error( "The storage does not support hierarchical access!" );
+ return false;
+ }
+
+ try
+ {
+ Object oDummyStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sPath, nMode, aPass );
+ Error( "The trying to open substream '" + sPath + "' must fail!" );
+ }
+ catch( WrongPasswordException wpe )
+ {
+ Error( "The substream '" + sPath + "' must not exist!" );
+ return false;
+ }
+ catch( Exception e )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public XStorage cloneStorage( XSingleServiceFactory xFactory, XStorage xStorage )
+ {
+ // create a copy of a last committed version of specified storage
+ XStorage xResult = null;
+ try
+ {
+ Object oTempStorage = xFactory.createInstance();
+ xResult = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xResult != null )
+ xStorage.copyLastCommitTo( xResult );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't clone storage, exception: " + e );
+ return null;
+ }
+
+ return xResult;
+ }
+
+ public XStorage cloneSubStorage( XSingleServiceFactory xFactory, XStorage xStorage, String sName )
+ {
+ // create a copy of a last committed version of specified substorage
+ XStorage xResult = null;
+ try
+ {
+ Object oTempStorage = xFactory.createInstance();
+ xResult = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
+ if ( xResult != null )
+ xStorage.copyStorageElementLastCommitTo( sName, xResult );
+ }
+ catch( Exception e )
+ {
+ Error( "Can't clone substorage '" + sName + "', exception: " + e );
+ return null;
+ }
+
+ return xResult;
+ }
+
+ public XStream cloneSubStream( XStorage xStorage, String sName )
+ {
+ // clone existing substream
+ try
+ {
+ XStream xStream = xStorage.cloneStreamElement( sName );
+ return xStream;
+ }
+ catch( Exception e )
+ {
+ Error( "Can't clone substream '" + sName + "', exception: " + e );
+ }
+
+ return null;
+ }
+
+ public XStream cloneEncrSubStream( XStorage xStorage, String sName, String sPass )
+ {
+ // clone existing substream
+ try
+ {
+ XStream xStream = xStorage.cloneEncryptedStreamElement( sName, sPass );
+ return xStream;
+ }
+ catch( Exception e )
+ {
+ Error( "Can't clone encrypted substream '" + sName + "', exception: " + e );
+ }
+
+ return null;
+ }
+
+ public void Error( String sError )
+ {
+ m_aLogWriter.println( m_sTestPrefix + "Error: " + sError );
+ }
+
+ public void Message( String sMessage )
+ {
+ m_aLogWriter.println( m_sTestPrefix + sMessage );
+ }
+}
+
diff --git a/package/qa/storages/makefile.mk b/package/qa/storages/makefile.mk
new file mode 100644
index 0000000000..d412846031
--- /dev/null
+++ b/package/qa/storages/makefile.mk
@@ -0,0 +1,109 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+
+
+PRJ = ..$/..
+TARGET = StorageUnitTest
+PRJNAME = package
+PACKAGE = complex$/storages
+
+# --- Settings -----------------------------------------------------
+.INCLUDE: settings.mk
+
+
+#----- compile .java files -----------------------------------------
+
+JARFILES = ridl.jar unoil.jar jurt.jar juh.jar java_uno.jar OOoRunner.jar
+
+JAVAFILES =\
+ StorageUnitTest.java\
+ StorageTest.java\
+ TestHelper.java\
+ BorderedStream.java\
+ Test01.java\
+ Test02.java\
+ Test03.java\
+ Test04.java\
+ Test05.java\
+ Test06.java\
+ Test07.java\
+ Test08.java\
+ Test09.java\
+ Test10.java\
+ Test11.java\
+ Test12.java\
+ Test13.java\
+ Test14.java\
+ Test15.java\
+ Test16.java\
+ Test17.java\
+ Test18.java\
+ RegressionTest_114358.java\
+ RegressionTest_i29169.java\
+ RegressionTest_i30400.java\
+ RegressionTest_i29321.java\
+ RegressionTest_i30677.java\
+ RegressionTest_i27773.java\
+ RegressionTest_i46848.java\
+ RegressionTest_i55821.java\
+ RegressionTest_i35095.java\
+ RegressionTest_i49755.java\
+ RegressionTest_i59886.java\
+ RegressionTest_i61909.java\
+ RegressionTest_i84234.java\
+ RegressionTest_125919.java
+
+JAVACLASSFILES = $(foreach,i,$(JAVAFILES) $(CLASSDIR)$/$(PACKAGE)$/$(i:b).class)
+
+#----- make a jar from compiled files ------------------------------
+
+MAXLINELENGTH = 100000
+
+JARCLASSDIRS = $(PACKAGE)
+JARTARGET = $(TARGET).jar
+JARCOMPRESS = TRUE
+
+# --- Parameters for the test --------------------------------------
+
+# start an office if the parameter is set for the makefile
+.IF "$(OFFICE)" == ""
+CT_APPEXECCOMMAND =
+.ELSE
+CT_APPEXECCOMMAND = -AppExecutionCommand "$(OFFICE)$/soffice --accept=socket,host=localhost,port=8100;urp;"
+.ENDIF
+
+# test base is java complex
+CT_TESTBASE = -TestBase java_complex
+
+# test looks something like the.full.package.TestName
+CT_TEST = -o $(PACKAGE:s\$/\.\).$(JAVAFILES:b)
+
+# start the runner application
+CT_APP = org.openoffice.Runner
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE: target.mk
+
+RUN: run
+
+run:
+ java -cp $(CLASSPATH) $(CT_APP) $(CT_TESTBASE) $(CT_APPEXECCOMMAND) $(CT_TEST)
+
+
diff --git a/package/source/manifest/ManifestDefines.hxx b/package/source/manifest/ManifestDefines.hxx
new file mode 100644
index 0000000000..dbe7b985b8
--- /dev/null
+++ b/package/source/manifest/ManifestDefines.hxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+inline constexpr OUString MANIFEST_NSPREFIX = u"manifest:"_ustr;
+inline constexpr OUString ELEMENT_MANIFEST = u"manifest:manifest"_ustr;
+inline constexpr OUString ATTRIBUTE_XMLNS = u"xmlns:manifest"_ustr;
+inline constexpr OUString ATTRIBUTE_XMLNS_LOEXT = u"xmlns:loext"_ustr;
+inline constexpr OUString MANIFEST_NAMESPACE = u"http://openoffice.org/2001/manifest"_ustr;
+inline constexpr OUString MANIFEST_OASIS_NAMESPACE = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"_ustr;
+inline constexpr OUString MANIFEST_LOEXT_NAMESPACE = u"urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"_ustr;
+inline constexpr OUString MANIFEST_DOCTYPE = u"<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">"_ustr;
+
+inline constexpr OUString ELEMENT_FILE_ENTRY = u"manifest:file-entry"_ustr;
+inline constexpr OUString ATTRIBUTE_FULL_PATH = u"manifest:full-path"_ustr;
+inline constexpr OUString ATTRIBUTE_VERSION = u"manifest:version"_ustr;
+inline constexpr OUString ATTRIBUTE_MEDIA_TYPE = u"manifest:media-type"_ustr;
+inline constexpr OUString ATTRIBUTE_SIZE = u"manifest:size"_ustr;
+inline constexpr OUString ELEMENT_MANIFEST_KEYINFO = u"loext:keyinfo"_ustr;
+inline constexpr OUString ELEMENT_ENCRYPTED_KEYINFO = u"loext:KeyInfo"_ustr;
+inline constexpr OUString ELEMENT_ENCRYPTEDKEY = u"loext:encrypted-key"_ustr;
+inline constexpr OUString ELEMENT_ENCRYPTIONMETHOD = u"loext:encryption-method"_ustr;
+inline constexpr OUString ELEMENT_PGPDATA = u"loext:PGPData"_ustr;
+inline constexpr OUString ELEMENT_PGPKEYID = u"loext:PGPKeyID"_ustr;
+inline constexpr OUString ELEMENT_PGPKEYPACKET = u"loext:PGPKeyPacket"_ustr;
+inline constexpr OUString ATTRIBUTE_ALGORITHM = u"loext:PGPAlgorithm"_ustr;
+inline constexpr OUString ELEMENT_CIPHERDATA = u"loext:CipherData"_ustr;
+inline constexpr OUString ELEMENT_CIPHERVALUE = u"loext:CipherValue"_ustr;
+inline constexpr OUString ELEMENT_MANIFEST13_KEYINFO = u"manifest:keyinfo"_ustr;
+inline constexpr OUString ELEMENT_ENCRYPTEDKEY13 = u"manifest:encrypted-key"_ustr;
+inline constexpr OUString ELEMENT_ENCRYPTIONMETHOD13 = u"manifest:encryption-method"_ustr;
+inline constexpr OUString ELEMENT_PGPDATA13 = u"manifest:PGPData"_ustr;
+inline constexpr OUString ELEMENT_PGPKEYID13 = u"manifest:PGPKeyID"_ustr;
+inline constexpr OUString ELEMENT_PGPKEYPACKET13 = u"manifest:PGPKeyPacket"_ustr;
+inline constexpr OUString ATTRIBUTE_ALGORITHM13 = u"manifest:PGPAlgorithm"_ustr;
+inline constexpr OUString ELEMENT_CIPHERDATA13 = u"manifest:CipherData"_ustr;
+inline constexpr OUString ELEMENT_CIPHERVALUE13 = u"manifest:CipherValue"_ustr;
+
+inline constexpr OUString ELEMENT_ENCRYPTION_DATA = u"manifest:encryption-data"_ustr;
+inline constexpr OUString ATTRIBUTE_CHECKSUM_TYPE = u"manifest:checksum-type"_ustr;
+inline constexpr OUString ATTRIBUTE_CHECKSUM = u"manifest:checksum"_ustr;
+
+inline constexpr OUString ELEMENT_ALGORITHM = u"manifest:algorithm"_ustr;
+inline constexpr OUString ATTRIBUTE_ALGORITHM_NAME = u"manifest:algorithm-name"_ustr;
+inline constexpr OUString ATTRIBUTE_INITIALISATION_VECTOR = u"manifest:initialisation-vector"_ustr;
+
+inline constexpr OUString ELEMENT_START_KEY_GENERATION = u"manifest:start-key-generation"_ustr;
+inline constexpr OUString ATTRIBUTE_START_KEY_GENERATION_NAME = u"manifest:start-key-generation-name"_ustr;
+inline constexpr OUString ATTRIBUTE_KEY_SIZE = u"manifest:key-size"_ustr;
+
+inline constexpr OUString ELEMENT_KEY_DERIVATION = u"manifest:key-derivation"_ustr;
+inline constexpr OUString ATTRIBUTE_KEY_DERIVATION_NAME = u"manifest:key-derivation-name"_ustr;
+inline constexpr OUString ATTRIBUTE_SALT = u"manifest:salt"_ustr;
+inline constexpr OUString ATTRIBUTE_ITERATION_COUNT = u"manifest:iteration-count"_ustr;
+inline constexpr OUString ATTRIBUTE_ARGON2_T_LO= u"loext:argon2-iterations"_ustr;
+inline constexpr OUString ATTRIBUTE_ARGON2_M_LO= u"loext:argon2-memory"_ustr;
+inline constexpr OUString ATTRIBUTE_ARGON2_P_LO= u"loext:argon2-lanes"_ustr;
+
+/// OFFICE-3708: wrong URL cited in ODF 1.2 and used since OOo 3.4 beta
+inline constexpr OUString SHA256_URL_ODF12 = u"http://www.w3.org/2000/09/xmldsig#sha256"_ustr;
+inline constexpr OUString SHA256_URL = u"http://www.w3.org/2001/04/xmlenc#sha256"_ustr;
+inline constexpr OUString SHA1_NAME = u"SHA1"_ustr;
+inline constexpr OUString SHA1_URL = u"http://www.w3.org/2000/09/xmldsig#sha1"_ustr;
+
+inline constexpr OUString SHA1_1K_NAME = u"SHA1/1K"_ustr;
+inline constexpr OUString SHA1_1K_URL = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#sha1-1k"_ustr;
+inline constexpr OUString SHA256_1K_URL = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#sha256-1k"_ustr;
+
+inline constexpr OUString BLOWFISH_NAME = u"Blowfish CFB"_ustr;
+inline constexpr OUString BLOWFISH_URL = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#blowfish"_ustr;
+inline constexpr OUString AES128_URL = u"http://www.w3.org/2001/04/xmlenc#aes128-cbc"_ustr;
+inline constexpr OUString AES192_URL = u"http://www.w3.org/2001/04/xmlenc#aes192-cbc"_ustr;
+inline constexpr OUString AES256_URL = u"http://www.w3.org/2001/04/xmlenc#aes256-cbc"_ustr;
+inline constexpr OUString AESGCM128_URL = u"http://www.w3.org/2009/xmlenc11#aes128-gcm"_ustr;
+inline constexpr OUString AESGCM192_URL = u"http://www.w3.org/2009/xmlenc11#aes192-gcm"_ustr;
+inline constexpr OUString AESGCM256_URL = u"http://www.w3.org/2009/xmlenc11#aes256-gcm"_ustr;
+
+inline constexpr OUString PBKDF2_NAME = u"PBKDF2"_ustr;
+inline constexpr OUString PGP_NAME = u"PGP"_ustr;
+inline constexpr OUString PBKDF2_URL = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#pbkdf2"_ustr;
+inline constexpr OUString ARGON2ID_URL = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.5#argon2id"_ustr;
+inline constexpr OUString ARGON2ID_URL_LO = u"urn:org:documentfoundation:names:experimental:office:manifest:argon2id"_ustr;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/manifest/ManifestExport.cxx b/package/source/manifest/ManifestExport.cxx
new file mode 100644
index 0000000000..1d51e223e4
--- /dev/null
+++ b/package/source/manifest/ManifestExport.cxx
@@ -0,0 +1,558 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/KDFID.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/uno/RuntimeException.hpp>
+
+#include "ManifestDefines.hxx"
+#include "ManifestExport.hxx"
+
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <comphelper/base64.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/attributelist.hxx>
+
+using namespace ::com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > const & xHandler, const uno::Sequence< uno::Sequence < beans::PropertyValue > >& rManList )
+{
+ static constexpr OUStringLiteral sKeyInfo ( u"KeyInfo" );
+ static constexpr OUStringLiteral sPgpKeyIDProperty ( u"KeyId" );
+ static constexpr OUStringLiteral sPgpKeyPacketProperty ( u"KeyPacket" );
+ static constexpr OUStringLiteral sCipherValueProperty ( u"CipherValue" );
+ static constexpr OUString sFullPathProperty ( u"FullPath"_ustr );
+ static constexpr OUString sVersionProperty ( u"Version"_ustr );
+ static constexpr OUString sMediaTypeProperty ( u"MediaType"_ustr );
+ static constexpr OUStringLiteral sIterationCountProperty ( u"IterationCount" );
+ static constexpr OUStringLiteral sDerivedKeySizeProperty ( u"DerivedKeySize" );
+ static constexpr OUStringLiteral sSaltProperty ( u"Salt" );
+ static constexpr OUStringLiteral sInitialisationVectorProperty( u"InitialisationVector" );
+ static constexpr OUStringLiteral sSizeProperty ( u"Size" );
+ static constexpr OUStringLiteral sDigestProperty ( u"Digest" );
+ static constexpr OUStringLiteral sEncryptionAlgProperty ( u"EncryptionAlgorithm" );
+ static constexpr OUStringLiteral sStartKeyAlgProperty ( u"StartKeyAlgorithm" );
+ static constexpr OUStringLiteral sDigestAlgProperty ( u"DigestAlgorithm" );
+
+ static constexpr OUString sWhiteSpace ( u" "_ustr );
+
+ const OUString sSHA256_URL_ODF12 ( SHA256_URL_ODF12 );
+ const OUString sSHA1_Name ( SHA1_NAME );
+
+ const OUString sSHA1_1k_Name ( SHA1_1K_NAME );
+ const OUString sSHA256_1k_URL ( SHA256_1K_URL );
+
+ const OUString sBlowfish_Name ( BLOWFISH_NAME );
+ const OUString sAES256_URL ( AES256_URL );
+
+ const OUString sPBKDF2_Name ( PBKDF2_NAME );
+ const OUString sPGP_Name ( PGP_NAME );
+
+ rtl::Reference<::comphelper::AttributeList> pRootAttrList = new ::comphelper::AttributeList;
+
+ // find the mediatype of the document if any
+ OUString aDocMediaType;
+ OUString aDocVersion;
+ bool isWholesomeEncryption(false);
+ const uno::Sequence<beans::PropertyValue>* pRootFolderPropSeq = nullptr;
+ for (const uno::Sequence < beans::PropertyValue >& rSequence : rManList)
+ {
+ OUString aMediaType;
+ OUString aPath;
+ OUString aVersion;
+
+ for (const beans::PropertyValue& rValue : rSequence)
+ {
+ if (rValue.Name == sMediaTypeProperty )
+ {
+ rValue.Value >>= aMediaType;
+ }
+ else if (rValue.Name == sFullPathProperty )
+ {
+ rValue.Value >>= aPath;
+ }
+ else if (rValue.Name == sVersionProperty )
+ {
+ rValue.Value >>= aVersion;
+ }
+
+ if ( !aPath.isEmpty() && !aMediaType.isEmpty() && !aVersion.isEmpty() )
+ break;
+ }
+
+ if ( aPath == "/" )
+ {
+ assert(aDocMediaType.isEmpty());
+ // unfortunately no aMediaType in some cases where non-documents
+ // are stored as StorageFormats::PACKAGE instead of sensible
+ // StorageFormats::ZIP, such as SvxXMLXTableExportComponent and
+ // SwXMLTextBlocks, which results in an empty "mimetype" etc but
+ // can't be easily fixed; try to exclude these cases by checking
+ // for aVersion, but of course then forgetting to set both version
+ // and type on an actual document can't be found :(
+ assert(!aMediaType.isEmpty() || aVersion.isEmpty());
+ aDocMediaType = aMediaType;
+ aDocVersion = aVersion;
+ pRootFolderPropSeq = &rSequence;
+ }
+
+ if (aPath == "encrypted-package")
+ {
+ isWholesomeEncryption = true;
+ assert(aDocMediaType.isEmpty() || aDocMediaType == aMediaType);
+ }
+ }
+ assert(pRootFolderPropSeq);
+
+ bool bProvideDTD = false;
+ bool bAcceptNonemptyVersion = false;
+ bool bStoreStartKeyGeneration = false;
+ if ( !aDocMediaType.isEmpty() )
+ {
+ if ( aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_CHART_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_TEMPLATE_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_CHART_TEMPLATE_ASCII
+ || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_TEMPLATE_ASCII )
+
+ {
+ // oasis format
+ pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS,
+ MANIFEST_OASIS_NAMESPACE );
+ bAcceptNonemptyVersion = true;
+ if ( aDocVersion.compareTo( ODFVER_012_TEXT ) >= 0 )
+ {
+ // this is ODF12 or later generation, let encrypted
+ // streams contain start-key-generation entry
+ bStoreStartKeyGeneration = true;
+ pRootAttrList->AddAttribute ( ATTRIBUTE_VERSION, aDocVersion );
+ // plus gpg4libre extensions - loext NS for that
+ pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS_LOEXT,
+ MANIFEST_LOEXT_NAMESPACE );
+ }
+ }
+ else
+ {
+ // even if it is no SO6 format the namespace must be specified
+ // thus SO6 format is used as default one
+ pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS,
+ MANIFEST_NAMESPACE );
+
+ bProvideDTD = true;
+ }
+ }
+
+ xHandler->startDocument();
+ uno::Reference < xml::sax::XExtendedDocumentHandler > xExtHandler ( xHandler, uno::UNO_QUERY );
+ if ( xExtHandler.is() && bProvideDTD )
+ {
+ xExtHandler->unknown ( MANIFEST_DOCTYPE );
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ }
+ xHandler->startElement( ELEMENT_MANIFEST, pRootAttrList );
+
+ const uno::Any *pKeyInfoProperty = nullptr;
+ if ( pRootFolderPropSeq )
+ {
+ // do we have package-wide encryption info?
+ for (const beans::PropertyValue& rValue : *pRootFolderPropSeq)
+ {
+ if (rValue.Name == sKeyInfo )
+ pKeyInfoProperty = &rValue.Value;
+ }
+
+ if ( pKeyInfoProperty )
+ {
+ // no start-key-generation needed, our session key has
+ // max size already
+ bStoreStartKeyGeneration = false;
+
+ // yeah, so that goes directly below the manifest:manifest
+ // element
+ OUStringBuffer aBuffer;
+
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ // ==== manifest:keyinfo & children
+ bool const isODF13(aDocVersion.compareTo(ODFVER_013_TEXT) >= 0);
+ if (!isODF13)
+ {
+ xHandler->startElement(ELEMENT_MANIFEST_KEYINFO, nullptr);
+ }
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ uno::Sequence< uno::Sequence < beans::NamedValue > > aKeyInfoSequence;
+ *pKeyInfoProperty >>= aKeyInfoSequence;
+ for (const uno::Sequence<beans::NamedValue>& rKeyInfoSequence : std::as_const(aKeyInfoSequence))
+ {
+ uno::Sequence < sal_Int8 > aPgpKeyID;
+ uno::Sequence < sal_Int8 > aPgpKeyPacket;
+ uno::Sequence < sal_Int8 > aCipherValue;
+ for (const beans::NamedValue& rNValue : rKeyInfoSequence)
+ {
+ if (rNValue.Name == sPgpKeyIDProperty )
+ rNValue.Value >>= aPgpKeyID;
+ else if (rNValue.Name == sPgpKeyPacketProperty )
+ rNValue.Value >>= aPgpKeyPacket;
+ else if (rNValue.Name == sCipherValueProperty )
+ rNValue.Value >>= aCipherValue;
+ }
+
+ if (aPgpKeyID.hasElements() && aCipherValue.hasElements() )
+ {
+ // ==== manifest:encrypted-key & children - one for each recipient
+ xHandler->startElement(isODF13 ? ELEMENT_ENCRYPTEDKEY13 : ELEMENT_ENCRYPTEDKEY, nullptr);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ rtl::Reference<::comphelper::AttributeList> pNewAttrList = new ::comphelper::AttributeList;
+ // TODO: the algorithm should rather be configurable
+ pNewAttrList->AddAttribute(
+ isODF13 ? ATTRIBUTE_ALGORITHM13 : ATTRIBUTE_ALGORITHM,
+ "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" );
+ xHandler->startElement(isODF13 ? ELEMENT_ENCRYPTIONMETHOD13 : ELEMENT_ENCRYPTIONMETHOD, pNewAttrList);
+ xHandler->endElement(isODF13 ? ELEMENT_ENCRYPTIONMETHOD13 : ELEMENT_ENCRYPTIONMETHOD);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ // note: the mismatch here corresponds to ODF 1.3 cs01 schema
+ xHandler->startElement(isODF13 ? ELEMENT_MANIFEST13_KEYINFO : ELEMENT_MANIFEST_KEYINFO, nullptr);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ xHandler->startElement(isODF13 ? ELEMENT_PGPDATA13 : ELEMENT_PGPDATA, nullptr);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ xHandler->startElement(isODF13 ? ELEMENT_PGPKEYID13 : ELEMENT_PGPKEYID, nullptr);
+ ::comphelper::Base64::encode(aBuffer, aPgpKeyID);
+ xHandler->characters( aBuffer.makeStringAndClear() );
+ xHandler->endElement(isODF13 ? ELEMENT_PGPKEYID13 : ELEMENT_PGPKEYID);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ // key packet is optional
+ if (aPgpKeyPacket.hasElements())
+ {
+ xHandler->startElement(isODF13 ? ELEMENT_PGPKEYPACKET13 : ELEMENT_PGPKEYPACKET, nullptr);
+ ::comphelper::Base64::encode(aBuffer, aPgpKeyPacket);
+ xHandler->characters( aBuffer.makeStringAndClear() );
+ xHandler->endElement(isODF13 ? ELEMENT_PGPKEYPACKET13 : ELEMENT_PGPKEYPACKET);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ }
+
+ xHandler->endElement(isODF13 ? ELEMENT_PGPDATA13 : ELEMENT_PGPDATA);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ xHandler->endElement(isODF13 ? ELEMENT_MANIFEST13_KEYINFO : ELEMENT_MANIFEST_KEYINFO);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ xHandler->startElement(isODF13 ? ELEMENT_CIPHERDATA13 : ELEMENT_CIPHERDATA, nullptr);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ xHandler->startElement(isODF13 ? ELEMENT_CIPHERVALUE13 : ELEMENT_CIPHERVALUE, nullptr);
+ ::comphelper::Base64::encode(aBuffer, aCipherValue);
+ xHandler->characters( aBuffer.makeStringAndClear() );
+ xHandler->endElement(isODF13 ? ELEMENT_CIPHERVALUE13 : ELEMENT_CIPHERVALUE);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ xHandler->endElement(isODF13 ? ELEMENT_CIPHERDATA13 : ELEMENT_CIPHERDATA);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ xHandler->endElement(isODF13 ? ELEMENT_ENCRYPTEDKEY13 : ELEMENT_ENCRYPTEDKEY);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ }
+ }
+
+ if (!isODF13)
+ {
+ xHandler->endElement(ELEMENT_MANIFEST_KEYINFO);
+ }
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ }
+ }
+
+ // now write individual file entries
+ for (const uno::Sequence<beans::PropertyValue>& rSequence : rManList)
+ {
+ if (&rSequence == pRootFolderPropSeq && isWholesomeEncryption)
+ {
+ continue; // no root document, but embedded package => omit
+ }
+ rtl::Reference<::comphelper::AttributeList> pAttrList = new ::comphelper::AttributeList;
+ OUString fullPath;
+ OUString aString;
+ const uno::Any *pVector = nullptr, *pSalt = nullptr, *pIterationCount = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
+ uno::Any const* pKDF = nullptr;
+ uno::Any const* pArgon2Args = nullptr;
+ for (const beans::PropertyValue& rValue : rSequence)
+ {
+ if (rValue.Name == sMediaTypeProperty )
+ {
+ rValue.Value >>= aString;
+ pAttrList->AddAttribute ( ATTRIBUTE_MEDIA_TYPE, aString );
+ }
+ else if (rValue.Name == sVersionProperty )
+ {
+ rValue.Value >>= aString;
+ // the version is stored only if it is not empty
+ if ( bAcceptNonemptyVersion && !aString.isEmpty() )
+ pAttrList->AddAttribute ( ATTRIBUTE_VERSION, aString );
+ }
+ else if (rValue.Name == sFullPathProperty )
+ {
+ rValue.Value >>= fullPath;
+ pAttrList->AddAttribute(ATTRIBUTE_FULL_PATH, fullPath);
+ }
+ else if (rValue.Name == sSizeProperty )
+ {
+ sal_Int64 nSize = 0;
+ rValue.Value >>= nSize;
+ pAttrList->AddAttribute ( ATTRIBUTE_SIZE, OUString::number( nSize ) );
+ }
+ else if (rValue.Name == sInitialisationVectorProperty )
+ pVector = &rValue.Value;
+ else if (rValue.Name == sSaltProperty )
+ pSalt = &rValue.Value;
+ else if (rValue.Name == sIterationCountProperty )
+ pIterationCount = &rValue.Value;
+ else if (rValue.Name == sDigestProperty )
+ pDigest = &rValue.Value;
+ else if (rValue.Name == sDigestAlgProperty )
+ pDigestAlg = &rValue.Value;
+ else if (rValue.Name == sEncryptionAlgProperty )
+ pEncryptAlg = &rValue.Value;
+ else if (rValue.Name == sStartKeyAlgProperty )
+ pStartKeyAlg = &rValue.Value;
+ else if (rValue.Name == sDerivedKeySizeProperty )
+ pDerivedKeySize = &rValue.Value;
+ else if (rValue.Name == "KeyDerivationFunction") {
+ pKDF = &rValue.Value;
+ } else if (rValue.Name == "Argon2Args") {
+ pArgon2Args = &rValue.Value;
+ }
+ }
+ assert(!fullPath.isEmpty());
+ if (isWholesomeEncryption)
+ { // there may be signatures in META-INF too
+ assert(fullPath == "encrypted-package" || fullPath.startsWith("META-INF/"));
+ }
+
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ xHandler->startElement( ELEMENT_FILE_ENTRY , pAttrList);
+ if (pVector && pEncryptAlg && pDerivedKeySize && pKDF
+ && ((pSalt && pStartKeyAlg && (pIterationCount || pArgon2Args))
+ || pKeyInfoProperty))
+ {
+ // ==== Encryption Data
+ rtl::Reference<::comphelper::AttributeList> pNewAttrList = new ::comphelper::AttributeList;
+ OUStringBuffer aBuffer;
+ uno::Sequence < sal_Int8 > aSequence;
+
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+
+ // ==== Digest
+ if (pDigest && pDigestAlg && pDigestAlg->hasValue())
+ {
+ OUString sChecksumType;
+ sal_Int32 nDigestAlgID = 0;
+ *pDigestAlg >>= nDigestAlgID;
+ if ( nDigestAlgID == xml::crypto::DigestID::SHA256_1K )
+ sChecksumType = sSHA256_1k_URL;
+ else if ( nDigestAlgID == xml::crypto::DigestID::SHA1_1K )
+ sChecksumType = sSHA1_1k_Name;
+ else
+ throw uno::RuntimeException( THROW_WHERE "Unexpected digest algorithm is provided!" );
+
+ pNewAttrList->AddAttribute(ATTRIBUTE_CHECKSUM_TYPE, sChecksumType);
+ *pDigest >>= aSequence;
+ ::comphelper::Base64::encode(aBuffer, aSequence);
+ pNewAttrList->AddAttribute(ATTRIBUTE_CHECKSUM, aBuffer.makeStringAndClear());
+ }
+
+ xHandler->startElement( ELEMENT_ENCRYPTION_DATA , pNewAttrList);
+
+ // ==== Algorithm
+ pNewAttrList = new ::comphelper::AttributeList;
+
+ sal_Int32 nEncAlgID = 0;
+ sal_Int32 nDerivedKeySize = 0;
+ *pEncryptAlg >>= nEncAlgID;
+ *pDerivedKeySize >>= nDerivedKeySize;
+
+ OUString sEncAlgName;
+ if ( nEncAlgID == xml::crypto::CipherID::AES_CBC_W3C_PADDING )
+ {
+ OSL_ENSURE( nDerivedKeySize, "Unexpected key size is provided!" );
+ if ( nDerivedKeySize != 32 )
+ throw uno::RuntimeException( THROW_WHERE "Unexpected key size is provided!" );
+
+ sEncAlgName = sAES256_URL;
+ }
+ else if (nEncAlgID == xml::crypto::CipherID::AES_GCM_W3C)
+ {
+ assert(bStoreStartKeyGeneration || pKeyInfoProperty);
+ SAL_WARN_IF(nDerivedKeySize != 32, "package.manifest", "Unexpected key size is provided!");
+ if (nDerivedKeySize != 32)
+ {
+ throw uno::RuntimeException(THROW_WHERE "Unexpected key size is provided!");
+ }
+ sEncAlgName = AESGCM256_URL;
+ }
+ else if ( nEncAlgID == xml::crypto::CipherID::BLOWFISH_CFB_8 )
+ {
+ sEncAlgName = sBlowfish_Name;
+ }
+ else
+ throw uno::RuntimeException( THROW_WHERE "Unexpected encryption algorithm is provided!" );
+
+ pNewAttrList->AddAttribute ( ATTRIBUTE_ALGORITHM_NAME, sEncAlgName );
+
+ *pVector >>= aSequence;
+ ::comphelper::Base64::encode(aBuffer, aSequence);
+ pNewAttrList->AddAttribute ( ATTRIBUTE_INITIALISATION_VECTOR, aBuffer.makeStringAndClear() );
+
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ xHandler->startElement( ELEMENT_ALGORITHM , pNewAttrList);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ xHandler->endElement( ELEMENT_ALGORITHM );
+
+ if ( bStoreStartKeyGeneration )
+ {
+ // ==== Start Key Generation
+ pNewAttrList = new ::comphelper::AttributeList;
+
+ OUString sStartKeyAlg;
+ OUString sStartKeySize;
+ sal_Int32 nStartKeyAlgID = 0;
+ *pStartKeyAlg >>= nStartKeyAlgID;
+ if ( nStartKeyAlgID == xml::crypto::DigestID::SHA256 )
+ {
+ if (nEncAlgID == xml::crypto::CipherID::AES_GCM_W3C)
+ { // new encryption is incompatible anyway, use W3C URL
+ sStartKeyAlg = SHA256_URL;
+ }
+ else // to interop with ODF <= 1.4 consumers use bad ODF URL
+ {
+ sStartKeyAlg = sSHA256_URL_ODF12;
+ }
+ aBuffer.append( sal_Int32(32) );
+ sStartKeySize = aBuffer.makeStringAndClear();
+ }
+ else if ( nStartKeyAlgID == xml::crypto::DigestID::SHA1 )
+ {
+ sStartKeyAlg = sSHA1_Name;
+ aBuffer.append( sal_Int32(20) );
+ sStartKeySize = aBuffer.makeStringAndClear();
+ }
+ else
+ throw uno::RuntimeException( THROW_WHERE "Unexpected start key algorithm is provided!" );
+
+ pNewAttrList->AddAttribute ( ATTRIBUTE_START_KEY_GENERATION_NAME, sStartKeyAlg );
+ pNewAttrList->AddAttribute ( ATTRIBUTE_KEY_SIZE, sStartKeySize );
+
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ xHandler->startElement( ELEMENT_START_KEY_GENERATION , pNewAttrList);
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ xHandler->endElement( ELEMENT_START_KEY_GENERATION );
+ }
+
+ // ==== Key Derivation
+ pNewAttrList = new ::comphelper::AttributeList;
+
+ if (pKeyInfoProperty)
+ {
+ assert(pKDF->get<sal_Int32>() == xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P);
+ pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
+ sPGP_Name);
+ }
+ else
+ {
+ if (pKDF->get<sal_Int32>() == xml::crypto::KDFID::Argon2id)
+ {
+ pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
+ ARGON2ID_URL_LO);
+
+ uno::Sequence<sal_Int32> args;
+ *pArgon2Args >>= args;
+ assert(args.getLength() == 3);
+ pNewAttrList->AddAttribute(ATTRIBUTE_ARGON2_T_LO, OUString::number(args[0]));
+ pNewAttrList->AddAttribute(ATTRIBUTE_ARGON2_M_LO, OUString::number(args[1]));
+ pNewAttrList->AddAttribute(ATTRIBUTE_ARGON2_P_LO, OUString::number(args[2]));
+ }
+ else
+ {
+ assert(pKDF->get<sal_Int32>() == xml::crypto::KDFID::PBKDF2);
+ pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
+ sPBKDF2_Name);
+
+ sal_Int32 nCount = 0;
+ *pIterationCount >>= nCount;
+ aBuffer.append(nCount);
+ pNewAttrList->AddAttribute(ATTRIBUTE_ITERATION_COUNT, aBuffer.makeStringAndClear());
+ }
+
+ *pSalt >>= aSequence;
+ ::comphelper::Base64::encode(aBuffer, aSequence);
+ pNewAttrList->AddAttribute ( ATTRIBUTE_SALT, aBuffer.makeStringAndClear() );
+ }
+
+ // ODF 1.3 specifies the default as 16 so have to write it for PGP
+ if (bStoreStartKeyGeneration || pKeyInfoProperty)
+ {
+ aBuffer.append(nDerivedKeySize);
+ pNewAttrList->AddAttribute(ATTRIBUTE_KEY_SIZE, aBuffer.makeStringAndClear());
+ }
+
+ xHandler->ignorableWhitespace(sWhiteSpace);
+ xHandler->startElement(ELEMENT_KEY_DERIVATION, pNewAttrList);
+ xHandler->ignorableWhitespace(sWhiteSpace);
+ xHandler->endElement(ELEMENT_KEY_DERIVATION);
+
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ xHandler->endElement( ELEMENT_ENCRYPTION_DATA );
+ }
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ xHandler->endElement( ELEMENT_FILE_ENTRY );
+ }
+ xHandler->ignorableWhitespace ( sWhiteSpace );
+ xHandler->endElement( ELEMENT_MANIFEST );
+ xHandler->endDocument();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/manifest/ManifestExport.hxx b/package/source/manifest/ManifestExport.hxx
new file mode 100644
index 0000000000..0148ea62c8
--- /dev/null
+++ b/package/source/manifest/ManifestExport.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_MANIFEST_MANIFESTEXPORT_HXX
+#define INCLUDED_PACKAGE_SOURCE_MANIFEST_MANIFESTEXPORT_HXX
+
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Reference.h>
+
+namespace com::sun::star {
+ namespace beans { struct PropertyValue;}
+ namespace xml::sax { class XDocumentHandler; }
+}
+class ManifestExport
+{
+public:
+ ManifestExport(css::uno::Reference < css::xml::sax::XDocumentHandler > const & xHandler, const css::uno::Sequence < css::uno::Sequence < css::beans::PropertyValue > > &rManList );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/manifest/ManifestImport.cxx b/package/source/manifest/ManifestImport.cxx
new file mode 100644
index 0000000000..f6f4ce36f4
--- /dev/null
+++ b/package/source/manifest/ManifestImport.cxx
@@ -0,0 +1,599 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ManifestImport.hxx"
+#include "ManifestDefines.hxx"
+#include <PackageConstants.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/xml/sax/XAttributeList.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/KDFID.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/base64.hxx>
+#include <comphelper/sequence.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star;
+
+constexpr OUStringLiteral gsFullPathProperty ( u"FullPath" );
+constexpr OUStringLiteral gsMediaTypeProperty ( u"MediaType" );
+constexpr OUStringLiteral gsVersionProperty ( u"Version" );
+constexpr OUStringLiteral gsIterationCountProperty ( u"IterationCount" );
+constexpr OUStringLiteral gsDerivedKeySizeProperty ( u"DerivedKeySize" );
+constexpr OUStringLiteral gsSaltProperty ( u"Salt" );
+constexpr OUStringLiteral gsInitialisationVectorProperty ( u"InitialisationVector" );
+constexpr OUStringLiteral gsSizeProperty ( u"Size" );
+constexpr OUStringLiteral gsDigestProperty ( u"Digest" );
+constexpr OUString gsEncryptionAlgProperty ( u"EncryptionAlgorithm"_ustr );
+constexpr OUString gsStartKeyAlgProperty ( u"StartKeyAlgorithm"_ustr );
+constexpr OUString gsDigestAlgProperty ( u"DigestAlgorithm"_ustr );
+
+ManifestImport::ManifestImport( std::vector < Sequence < PropertyValue > > & rNewManVector )
+ : bIgnoreEncryptData ( false )
+ , bPgpEncryption ( false )
+ , nDerivedKeySize( 0 )
+ , rManVector ( rNewManVector )
+{
+ aStack.reserve( 10 );
+}
+
+ManifestImport::~ManifestImport()
+{
+}
+
+void SAL_CALL ManifestImport::startDocument( )
+{
+}
+
+void SAL_CALL ManifestImport::endDocument( )
+{
+}
+
+void ManifestImport::doFileEntry(StringHashMap &rConvertedAttribs)
+{
+ aSequence.resize(PKG_SIZE_ENCR_MNFST);
+
+ aSequence[PKG_MNFST_FULLPATH].Name = gsFullPathProperty;
+ aSequence[PKG_MNFST_FULLPATH].Value <<= rConvertedAttribs[ATTRIBUTE_FULL_PATH];
+ aSequence[PKG_MNFST_MEDIATYPE].Name = gsMediaTypeProperty;
+ aSequence[PKG_MNFST_MEDIATYPE].Value <<= rConvertedAttribs[ATTRIBUTE_MEDIA_TYPE];
+
+ OUString sVersion = rConvertedAttribs[ATTRIBUTE_VERSION];
+ if ( sVersion.getLength() ) {
+ aSequence[PKG_MNFST_VERSION].Name = gsVersionProperty;
+ aSequence[PKG_MNFST_VERSION].Value <<= sVersion;
+ }
+
+ OUString sSize = rConvertedAttribs[ATTRIBUTE_SIZE];
+ if ( sSize.getLength() ) {
+ sal_Int64 nSize = sSize.toInt64();
+ aSequence[PKG_MNFST_UCOMPSIZE].Name = gsSizeProperty;
+ aSequence[PKG_MNFST_UCOMPSIZE].Value <<= nSize;
+ }
+}
+
+void ManifestImport::doEncryptedKey(StringHashMap &)
+{
+ aKeyInfoSequence.clear();
+ aKeyInfoSequence.resize(3);
+}
+
+void ManifestImport::doEncryptionMethod(StringHashMap &rConvertedAttribs,
+ const OUString& rAlgoAttrName)
+{
+ OUString aString = rConvertedAttribs[rAlgoAttrName];
+ if ( aKeyInfoSequence.size() != 3
+ || aString != "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" )
+ {
+ bIgnoreEncryptData = true;
+ }
+}
+
+void ManifestImport::doEncryptedCipherValue()
+{
+ if ( aKeyInfoSequence.size() == 3 )
+ {
+ aKeyInfoSequence[2].Name = "CipherValue";
+ uno::Sequence < sal_Int8 > aDecodeBuffer;
+ ::comphelper::Base64::decode(aDecodeBuffer, aCurrentCharacters);
+ aKeyInfoSequence[2].Value <<= aDecodeBuffer;
+ aCurrentCharacters.setLength(0); // consumed
+ }
+ else
+ bIgnoreEncryptData = true;
+}
+
+void ManifestImport::doEncryptedKeyId()
+{
+ if ( aKeyInfoSequence.size() == 3 )
+ {
+ aKeyInfoSequence[0].Name = "KeyId";
+ uno::Sequence < sal_Int8 > aDecodeBuffer;
+ ::comphelper::Base64::decode(aDecodeBuffer, aCurrentCharacters);
+ aKeyInfoSequence[0].Value <<= aDecodeBuffer;
+ aCurrentCharacters.setLength(0); // consumed
+ }
+ else
+ bIgnoreEncryptData = true;
+}
+
+void ManifestImport::doEncryptedKeyPacket()
+{
+ if ( aKeyInfoSequence.size() == 3 )
+ {
+ aKeyInfoSequence[1].Name = "KeyPacket";
+ uno::Sequence < sal_Int8 > aDecodeBuffer;
+ ::comphelper::Base64::decode(aDecodeBuffer, aCurrentCharacters);
+ aKeyInfoSequence[1].Value <<= aDecodeBuffer;
+ aCurrentCharacters.setLength(0); // consumed
+ }
+ else
+ bIgnoreEncryptData = true;
+}
+
+void ManifestImport::doEncryptionData(StringHashMap &rConvertedAttribs)
+{
+ // If this element exists, then this stream is encrypted and we need
+ // to import the initialisation vector, salt and iteration count used
+ nDerivedKeySize = 0;
+ OUString aString = rConvertedAttribs[ATTRIBUTE_CHECKSUM_TYPE];
+ if ( bIgnoreEncryptData )
+ return;
+
+ if ( aString == SHA1_1K_NAME || aString == SHA1_1K_URL ) {
+ aSequence[PKG_MNFST_DIGESTALG].Name = gsDigestAlgProperty;
+ aSequence[PKG_MNFST_DIGESTALG].Value <<= xml::crypto::DigestID::SHA1_1K;
+ } else if ( aString == SHA256_1K_URL ) {
+ aSequence[PKG_MNFST_DIGESTALG].Name = gsDigestAlgProperty;
+ aSequence[PKG_MNFST_DIGESTALG].Value <<= xml::crypto::DigestID::SHA256_1K;
+ }
+ // note: digest is now *optional* - expected not to be used with AEAD
+
+ if (aSequence[PKG_MNFST_DIGESTALG].Value.hasValue()) {
+ aString = rConvertedAttribs[ATTRIBUTE_CHECKSUM];
+ uno::Sequence < sal_Int8 > aDecodeBuffer;
+ ::comphelper::Base64::decode(aDecodeBuffer, aString);
+ aSequence[PKG_MNFST_DIGEST].Name = gsDigestProperty;
+ aSequence[PKG_MNFST_DIGEST].Value <<= aDecodeBuffer;
+ }
+}
+
+void ManifestImport::doAlgorithm(StringHashMap &rConvertedAttribs)
+{
+ if ( bIgnoreEncryptData )
+ return;
+
+ OUString aString = rConvertedAttribs[ATTRIBUTE_ALGORITHM_NAME];
+ if ( aString == BLOWFISH_NAME || aString == BLOWFISH_URL ) {
+ aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+ aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::BLOWFISH_CFB_8;
+ } else if (aString == AESGCM256_URL) {
+ aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+ aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::AES_GCM_W3C;
+ SAL_INFO_IF(nDerivedKeySize != 0 && nDerivedKeySize != 32, "package.manifest", "Unexpected derived key length!");
+ nDerivedKeySize = 32;
+ } else if (aString == AESGCM192_URL) {
+ aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+ aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::AES_GCM_W3C;
+ SAL_INFO_IF(nDerivedKeySize != 0 && nDerivedKeySize != 24, "package.manifest", "Unexpected derived key length!");
+ nDerivedKeySize = 24;
+ } else if (aString == AESGCM128_URL) {
+ aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+ aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::AES_GCM_W3C;
+ SAL_INFO_IF(nDerivedKeySize != 0 && nDerivedKeySize != 16, "package.manifest", "Unexpected derived key length!");
+ nDerivedKeySize = 16;
+ } else if ( aString == AES256_URL ) {
+ aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+ aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING;
+ OSL_ENSURE( !nDerivedKeySize || nDerivedKeySize == 32, "Unexpected derived key length!" );
+ nDerivedKeySize = 32;
+ } else if ( aString == AES192_URL ) {
+ aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+ aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING;
+ OSL_ENSURE( !nDerivedKeySize || nDerivedKeySize == 24, "Unexpected derived key length!" );
+ nDerivedKeySize = 24;
+ } else if ( aString == AES128_URL ) {
+ aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+ aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING;
+ OSL_ENSURE( !nDerivedKeySize || nDerivedKeySize == 16, "Unexpected derived key length!" );
+ nDerivedKeySize = 16;
+ } else
+ bIgnoreEncryptData = true;
+
+ if ( !bIgnoreEncryptData ) {
+ aString = rConvertedAttribs[ATTRIBUTE_INITIALISATION_VECTOR];
+ uno::Sequence < sal_Int8 > aDecodeBuffer;
+ ::comphelper::Base64::decode(aDecodeBuffer, aString);
+ aSequence[PKG_MNFST_INIVECTOR].Name = gsInitialisationVectorProperty;
+ aSequence[PKG_MNFST_INIVECTOR].Value <<= aDecodeBuffer;
+ }
+}
+
+void ManifestImport::doKeyDerivation(StringHashMap &rConvertedAttribs)
+{
+ if ( bIgnoreEncryptData )
+ return;
+
+ OUString aString = rConvertedAttribs[ATTRIBUTE_KEY_DERIVATION_NAME];
+ if (aString == PBKDF2_NAME || aString == PBKDF2_URL
+ || aString == ARGON2ID_URL || aString == ARGON2ID_URL_LO)
+ {
+ aSequence[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ if (aString == ARGON2ID_URL || aString == ARGON2ID_URL_LO)
+ {
+ aSequence[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::Argon2id;
+
+ aString = rConvertedAttribs[ATTRIBUTE_ARGON2_T_LO];
+ sal_Int32 const t(aString.toInt32());
+ aString = rConvertedAttribs[ATTRIBUTE_ARGON2_M_LO];
+ sal_Int32 const m(aString.toInt32());
+ aString = rConvertedAttribs[ATTRIBUTE_ARGON2_P_LO];
+ sal_Int32 const p(aString.toInt32());
+ if (0 < t && 0 < m && 0 < p)
+ {
+ aSequence[PKG_MNFST_ARGON2ARGS].Name = "Argon2Args";
+ aSequence[PKG_MNFST_ARGON2ARGS].Value <<= uno::Sequence{t,m,p};
+ }
+ else
+ {
+ SAL_INFO("package.manifest", "invalid argon2 arguments");
+ bIgnoreEncryptData = true;
+ }
+ }
+ else
+ {
+ aSequence[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PBKDF2;
+
+ aString = rConvertedAttribs[ATTRIBUTE_ITERATION_COUNT];
+ aSequence[PKG_MNFST_ITERATION].Name = gsIterationCountProperty;
+ aSequence[PKG_MNFST_ITERATION].Value <<= aString.toInt32();
+ }
+
+ aString = rConvertedAttribs[ATTRIBUTE_SALT];
+ uno::Sequence < sal_Int8 > aDecodeBuffer;
+ ::comphelper::Base64::decode(aDecodeBuffer, aString);
+ aSequence[PKG_MNFST_SALT].Name = gsSaltProperty;
+ aSequence[PKG_MNFST_SALT].Value <<= aDecodeBuffer;
+
+ aString = rConvertedAttribs[ATTRIBUTE_KEY_SIZE];
+ if ( aString.getLength() ) {
+ sal_Int32 nKey = aString.toInt32();
+ OSL_ENSURE( !nDerivedKeySize || nKey == nDerivedKeySize , "Provided derived key length differs from the expected one!" );
+ nDerivedKeySize = nKey;
+ } else if ( !nDerivedKeySize )
+ nDerivedKeySize = 16;
+ else if ( nDerivedKeySize != 16 )
+ OSL_ENSURE( false, "Default derived key length differs from the expected one!" );
+
+ aSequence[PKG_MNFST_DERKEYSIZE].Name = gsDerivedKeySizeProperty;
+ aSequence[PKG_MNFST_DERKEYSIZE].Value <<= nDerivedKeySize;
+ } else if ( bPgpEncryption ) {
+ if (aString == "PGP") {
+ aSequence[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ aSequence[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
+ } else {
+ bIgnoreEncryptData = true;
+ }
+ } else
+ bIgnoreEncryptData = true;
+}
+
+void ManifestImport::doStartKeyAlg(StringHashMap &rConvertedAttribs)
+{
+ OUString aString = rConvertedAttribs[ATTRIBUTE_START_KEY_GENERATION_NAME];
+ if (aString == SHA256_URL || aString == SHA256_URL_ODF12) {
+ aSequence[PKG_MNFST_STARTALG].Name = gsStartKeyAlgProperty;
+ aSequence[PKG_MNFST_STARTALG].Value <<= xml::crypto::DigestID::SHA256;
+ } else if ( aString == SHA1_NAME || aString == SHA1_URL ) {
+ aSequence[PKG_MNFST_STARTALG].Name = gsStartKeyAlgProperty;
+ aSequence[PKG_MNFST_STARTALG].Value <<= xml::crypto::DigestID::SHA1;
+ } else
+ bIgnoreEncryptData = true;
+}
+
+void SAL_CALL ManifestImport::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
+{
+ StringHashMap aConvertedAttribs;
+ OUString aConvertedName = PushNameAndNamespaces( aName, xAttribs, aConvertedAttribs );
+
+ size_t nLevel = aStack.size();
+
+ assert(nLevel >= 1);
+
+ switch (nLevel) {
+ case 1: {
+ if (aConvertedName != ELEMENT_MANIFEST) //manifest:manifest
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 2: {
+ if (aConvertedName == ELEMENT_FILE_ENTRY) //manifest:file-entry
+ doFileEntry(aConvertedAttribs);
+ else if (aConvertedName == ELEMENT_MANIFEST_KEYINFO) //loext:keyinfo
+ ;
+ else if (aConvertedName == ELEMENT_ENCRYPTEDKEY13) //manifest:encrypted-key
+ doEncryptedKey(aConvertedAttribs);
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 3: {
+ ManifestStack::reverse_iterator aIter = aStack.rbegin();
+ ++aIter;
+
+ if (!aIter->m_bValid)
+ aStack.back().m_bValid = false;
+ else if (aConvertedName == ELEMENT_ENCRYPTION_DATA) //manifest:encryption-data
+ doEncryptionData(aConvertedAttribs);
+ else if (aConvertedName == ELEMENT_ENCRYPTEDKEY) //loext:encrypted-key
+ doEncryptedKey(aConvertedAttribs);
+ else if (aConvertedName == ELEMENT_ENCRYPTIONMETHOD13) //manifest:encryption-method
+ doEncryptionMethod(aConvertedAttribs, ATTRIBUTE_ALGORITHM13);
+ else if (aConvertedName == ELEMENT_MANIFEST13_KEYINFO) //manifest:keyinfo
+ ;
+ else if (aConvertedName == ELEMENT_CIPHERDATA13) //manifest:CipherData
+ ;
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 4: {
+ ManifestStack::reverse_iterator aIter = aStack.rbegin();
+ ++aIter;
+
+ if (!aIter->m_bValid)
+ aStack.back().m_bValid = false;
+ else if (aConvertedName == ELEMENT_ALGORITHM) //manifest:algorithm,
+ doAlgorithm(aConvertedAttribs);
+ else if (aConvertedName == ELEMENT_KEY_DERIVATION) //manifest:key-derivation,
+ doKeyDerivation(aConvertedAttribs);
+ else if (aConvertedName == ELEMENT_START_KEY_GENERATION) //manifest:start-key-generation
+ doStartKeyAlg(aConvertedAttribs);
+ else if (aConvertedName == ELEMENT_ENCRYPTIONMETHOD) //loext:encryption-method
+ doEncryptionMethod(aConvertedAttribs, ATTRIBUTE_ALGORITHM);
+ else if (aConvertedName == ELEMENT_ENCRYPTED_KEYINFO) //loext:KeyInfo
+ ;
+ else if (aConvertedName == ELEMENT_CIPHERDATA) //loext:CipherData
+ ;
+ else if (aConvertedName == ELEMENT_PGPDATA13) //manifest:PGPData
+ ;
+ else if (aConvertedName == ELEMENT_CIPHERVALUE13) //manifest:CipherValue
+ // ciphervalue action happens on endElement
+ aCurrentCharacters = "";
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 5: {
+ ManifestStack::reverse_iterator aIter = aStack.rbegin();
+ ++aIter;
+
+ if (!aIter->m_bValid)
+ aStack.back().m_bValid = false;
+ else if (aConvertedName == ELEMENT_PGPDATA) //loext:PGPData
+ ;
+ else if (aConvertedName == ELEMENT_CIPHERVALUE) //loext:CipherValue
+ // ciphervalue action happens on endElement
+ aCurrentCharacters = "";
+ else if (aConvertedName == ELEMENT_PGPKEYID13) //manifest:PGPKeyID
+ // ciphervalue action happens on endElement
+ aCurrentCharacters = "";
+ else if (aConvertedName == ELEMENT_PGPKEYPACKET13) //manifest:PGPKeyPacket
+ // ciphervalue action happens on endElement
+ aCurrentCharacters = "";
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 6: {
+ ManifestStack::reverse_iterator aIter = aStack.rbegin();
+ ++aIter;
+
+ if (!aIter->m_bValid)
+ aStack.back().m_bValid = false;
+ else if (aConvertedName == ELEMENT_PGPKEYID) //loext:PGPKeyID
+ // ciphervalue action happens on endElement
+ aCurrentCharacters = "";
+ else if (aConvertedName == ELEMENT_PGPKEYPACKET) //loext:PGPKeyPacket
+ // ciphervalue action happens on endElement
+ aCurrentCharacters = "";
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ default:
+ aStack.back().m_bValid = false;
+ break;
+ }
+}
+
+namespace
+{
+bool isEmpty(const css::beans::PropertyValue &rProp)
+{
+ return rProp.Name.isEmpty();
+}
+}
+
+void SAL_CALL ManifestImport::endElement( const OUString& aName )
+{
+ size_t nLevel = aStack.size();
+
+ assert(nLevel >= 1);
+
+ OUString aConvertedName = ConvertName( aName );
+ if ( aStack.empty() || aStack.rbegin()->m_aConvertedName != aConvertedName )
+ return;
+
+ if ( aConvertedName == ELEMENT_FILE_ENTRY && aStack.back().m_bValid ) {
+ // the first entry gets KeyInfo element if any, for PGP encryption
+ if (!bIgnoreEncryptData && !aKeys.empty() && rManVector.empty())
+ {
+ aSequence[PKG_MNFST_KEYINFO].Name = "KeyInfo";
+ aSequence[PKG_MNFST_KEYINFO].Value <<= comphelper::containerToSequence(aKeys);
+ }
+ std::erase_if(aSequence, isEmpty);
+
+ bIgnoreEncryptData = false;
+ rManVector.push_back ( comphelper::containerToSequence(aSequence) );
+
+ aSequence.clear();
+ }
+ else if ( (aConvertedName == ELEMENT_ENCRYPTEDKEY
+ || aConvertedName == ELEMENT_ENCRYPTEDKEY13)
+ && aStack.back().m_bValid ) {
+ if ( !bIgnoreEncryptData )
+ {
+ aKeys.push_back( comphelper::containerToSequence(aKeyInfoSequence) );
+ bPgpEncryption = true;
+ }
+ aKeyInfoSequence.clear();
+ }
+
+ // end element handling for elements with cdata
+ switch (nLevel) {
+ case 4: {
+ if (aConvertedName == ELEMENT_CIPHERVALUE13) //manifest:CipherValue
+ doEncryptedCipherValue();
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 5: {
+ if (aConvertedName == ELEMENT_CIPHERVALUE) //loext:CipherValue
+ doEncryptedCipherValue();
+ else if (aConvertedName == ELEMENT_PGPKEYID13) //manifest:PGPKeyID
+ doEncryptedKeyId();
+ else if (aConvertedName == ELEMENT_PGPKEYPACKET13) //manifest:PGPKeyPacket
+ doEncryptedKeyPacket();
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 6: {
+ if (aConvertedName == ELEMENT_PGPKEYID) //loext:PGPKeyID
+ doEncryptedKeyId();
+ else if (aConvertedName == ELEMENT_PGPKEYPACKET) //loext:PGPKeyPacket
+ doEncryptedKeyPacket();
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ }
+
+ aStack.pop_back();
+}
+
+void SAL_CALL ManifestImport::characters( const OUString& aChars )
+{
+ aCurrentCharacters.append(aChars);
+}
+
+void SAL_CALL ManifestImport::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
+{
+}
+
+void SAL_CALL ManifestImport::processingInstruction( const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+void SAL_CALL ManifestImport::setDocumentLocator( const uno::Reference< xml::sax::XLocator >& /*xLocator*/ )
+{
+}
+
+OUString ManifestImport::PushNameAndNamespaces( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs, StringHashMap& o_aConvertedAttribs )
+{
+ StringHashMap aNamespaces;
+ ::std::vector< ::std::pair< OUString, OUString > > aAttribsStrs;
+
+ if ( xAttribs.is() ) {
+ sal_Int16 nAttrCount = xAttribs.is() ? xAttribs->getLength() : 0;
+ aAttribsStrs.reserve( nAttrCount );
+
+ for( sal_Int16 nInd = 0; nInd < nAttrCount; nInd++ ) {
+ OUString aAttrName = xAttribs->getNameByIndex( nInd );
+ OUString aAttrValue = xAttribs->getValueByIndex( nInd );
+ if ( aAttrName.getLength() >= 5
+ && aAttrName.startsWith("xmlns")
+ && ( aAttrName.getLength() == 5 || aAttrName[5] == ':' ) ) {
+ // this is a namespace declaration
+ OUString aNsName( ( aAttrName.getLength() == 5 ) ? OUString() : aAttrName.copy( 6 ) );
+ aNamespaces[aNsName] = aAttrValue;
+ } else {
+ // this is no namespace declaration
+ aAttribsStrs.emplace_back( aAttrName, aAttrValue );
+ }
+ }
+ }
+
+ OUString aConvertedName = ConvertNameWithNamespace( aName, aNamespaces );
+ if ( !aConvertedName.getLength() )
+ aConvertedName = ConvertName( aName );
+
+ aStack.emplace_back( aConvertedName, std::move(aNamespaces) );
+
+ for (const std::pair<OUString,OUString> & rAttribsStr : aAttribsStrs) {
+ // convert the attribute names on filling
+ o_aConvertedAttribs[ConvertName( rAttribsStr.first )] = rAttribsStr.second;
+ }
+
+ return aConvertedName;
+}
+
+OUString ManifestImport::ConvertNameWithNamespace( const OUString& aName, const StringHashMap& aNamespaces )
+{
+ OUString aNsAlias;
+ OUString aPureName = aName;
+
+ sal_Int32 nInd = aName.indexOf( ':' );
+ if ( nInd != -1 && nInd < aName.getLength() ) {
+ aNsAlias = aName.copy( 0, nInd );
+ aPureName = aName.copy( nInd + 1 );
+ }
+
+ OUString aResult;
+
+ StringHashMap::const_iterator aIter = aNamespaces.find( aNsAlias );
+ if ( aIter != aNamespaces.end()
+ && ( aIter->second == MANIFEST_NAMESPACE || aIter->second == MANIFEST_OASIS_NAMESPACE ) ) {
+ // no check for manifest.xml consistency currently since the old versions have supported inconsistent documents as well
+ aResult = MANIFEST_NSPREFIX + aPureName;
+ }
+
+ return aResult;
+}
+
+OUString ManifestImport::ConvertName( const OUString& aName )
+{
+ OUString aConvertedName;
+ for ( ManifestStack::reverse_iterator aIter = aStack.rbegin(); !aConvertedName.getLength() && aIter != aStack.rend(); ++aIter ) {
+ if ( !aIter->m_aNamespaces.empty() )
+ aConvertedName = ConvertNameWithNamespace( aName, aIter->m_aNamespaces );
+ }
+
+ if ( !aConvertedName.getLength() )
+ aConvertedName = aName;
+
+ return aConvertedName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/manifest/ManifestImport.hxx b/package/source/manifest/ManifestImport.hxx
new file mode 100644
index 0000000000..fd86e02e4f
--- /dev/null
+++ b/package/source/manifest/ManifestImport.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_MANIFEST_MANIFESTIMPORT_HXX
+#define INCLUDED_PACKAGE_SOURCE_MANIFEST_MANIFESTIMPORT_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+#include <rtl/ustrbuf.hxx>
+
+namespace com::sun::star {
+ namespace xml::sax { class XAttributeList; }
+ namespace beans { struct PropertyValue; }
+}
+
+typedef std::unordered_map< OUString, OUString > StringHashMap;
+
+struct ManifestScopeEntry
+{
+ OUString m_aConvertedName;
+ StringHashMap m_aNamespaces;
+ bool m_bValid;
+
+ ManifestScopeEntry( OUString aConvertedName, StringHashMap&& aNamespaces )
+ : m_aConvertedName(std::move( aConvertedName ))
+ , m_aNamespaces( std::move(aNamespaces) )
+ , m_bValid( true )
+ {}
+};
+
+typedef ::std::vector< ManifestScopeEntry > ManifestStack;
+
+class ManifestImport final : public cppu::WeakImplHelper < css::xml::sax::XDocumentHandler >
+{
+ std::vector< css::beans::NamedValue > aKeyInfoSequence;
+ std::vector< css::uno::Sequence< css::beans::NamedValue > > aKeys;
+ std::vector< css::beans::PropertyValue > aSequence;
+ OUStringBuffer aCurrentCharacters{64};
+ ManifestStack aStack;
+ bool bIgnoreEncryptData;
+ bool bPgpEncryption;
+ sal_Int32 nDerivedKeySize;
+ ::std::vector < css::uno::Sequence < css::beans::PropertyValue > > & rManVector;
+
+
+ OUString PushNameAndNamespaces( const OUString& aName,
+ const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs,
+ StringHashMap& o_aConvertedAttribs );
+ static OUString ConvertNameWithNamespace( const OUString& aName, const StringHashMap& aNamespaces );
+ OUString ConvertName( const OUString& aName );
+
+public:
+ ManifestImport( std::vector < css::uno::Sequence < css::beans::PropertyValue > > & rNewVector );
+ virtual ~ManifestImport() override;
+ virtual void SAL_CALL startDocument( ) override;
+ virtual void SAL_CALL endDocument( ) override;
+ virtual void SAL_CALL startElement( const OUString& aName, const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs ) override;
+ virtual void SAL_CALL endElement( const OUString& aName ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+ virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) override;
+ virtual void SAL_CALL processingInstruction( const OUString& aTarget, const OUString& aData ) override;
+ virtual void SAL_CALL setDocumentLocator( const css::uno::Reference< css::xml::sax::XLocator >& xLocator ) override;
+
+private:
+ /// @throws css::uno::RuntimeException
+ void doFileEntry(StringHashMap &rConvertedAttribs);
+ /// @throws css::uno::RuntimeException
+ void doEncryptionData(StringHashMap &rConvertedAttribs);
+ /// @throws css::uno::RuntimeException
+ void doAlgorithm(StringHashMap &rConvertedAttribs);
+ /// @throws css::uno::RuntimeException
+ void doKeyDerivation(StringHashMap &rConvertedAttribs);
+ /// @throws css::uno::RuntimeException
+ void doStartKeyAlg(StringHashMap &rConvertedAttribs);
+ void doEncryptedKey(StringHashMap &);
+ void doEncryptionMethod(StringHashMap &, const OUString &);
+ void doEncryptedCipherValue();
+ void doEncryptedKeyId();
+ void doEncryptedKeyPacket();
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/manifest/ManifestReader.cxx b/package/source/manifest/ManifestReader.cxx
new file mode 100644
index 0000000000..2a60feff02
--- /dev/null
+++ b/package/source/manifest/ManifestReader.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ManifestReader.hxx"
+#include "ManifestImport.hxx"
+#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <vector>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::registry;
+using namespace ::com::sun::star::packages;
+using namespace ::com::sun::star::xml::sax;
+
+ManifestReader::ManifestReader( const Reference < XComponentContext > & xContext )
+: m_xContext ( xContext )
+{
+}
+ManifestReader::~ManifestReader()
+{
+}
+Sequence< Sequence< PropertyValue > > SAL_CALL ManifestReader::readManifestSequence( const Reference< XInputStream >& rStream )
+{
+ Sequence < Sequence < PropertyValue > > aManifestSequence;
+ Reference < XParser > xParser = Parser::create(m_xContext);
+ try
+ {
+ std::vector < Sequence < PropertyValue > > aManVector;
+ Reference < XDocumentHandler > xFilter = new ManifestImport( aManVector );
+ InputSource aParserInput;
+ aParserInput.aInputStream = rStream;
+ aParserInput.sSystemId = "META-INF/manifest.xml";
+ xParser->setDocumentHandler ( xFilter );
+ xParser->parseStream( aParserInput );
+ aManifestSequence = comphelper::containerToSequence(aManVector);
+ }
+ catch (const SAXParseException&)
+ {
+ TOOLS_WARN_EXCEPTION("package", "ignoring");
+ }
+ catch (const SAXException&)
+ {
+ TOOLS_WARN_EXCEPTION("package", "ignoring");
+ }
+ catch (const IOException&)
+ {
+ TOOLS_WARN_EXCEPTION("package", "ignoring");
+ }
+ xParser->setDocumentHandler ( Reference < XDocumentHandler > () );
+ return aManifestSequence;
+}
+// Component functions
+
+
+OUString ManifestReader::getImplementationName()
+{
+ return "com.sun.star.packages.manifest.comp.ManifestReader";
+}
+
+sal_Bool SAL_CALL ManifestReader::supportsService(OUString const & rServiceName)
+{
+ return cppu::supportsService(this, rServiceName );
+}
+
+Sequence < OUString > ManifestReader::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.manifest.ManifestReader" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+package_ManifestReader_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ManifestReader(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/manifest/ManifestReader.hxx b/package/source/manifest/ManifestReader.hxx
new file mode 100644
index 0000000000..f85644162d
--- /dev/null
+++ b/package/source/manifest/ManifestReader.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_MANIFEST_MANIFESTREADER_HXX
+#define INCLUDED_PACKAGE_SOURCE_MANIFEST_MANIFESTREADER_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/packages/manifest/XManifestReader.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+namespace com::sun::star {
+ namespace lang { class XSingleServiceFactory; }
+ namespace uno { class XComponentContext; }
+}
+
+class ManifestReader: public ::cppu::WeakImplHelper
+<
+ css::packages::manifest::XManifestReader,
+ css::lang::XServiceInfo
+>
+{
+private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+public:
+ ManifestReader( const css::uno::Reference< css::uno::XComponentContext > & xContext );
+ virtual ~ManifestReader() override;
+
+ // XManifestReader
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL readManifestSequence( const css::uno::Reference< css::io::XInputStream >& rStream ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/manifest/ManifestWriter.cxx b/package/source/manifest/ManifestWriter.cxx
new file mode 100644
index 0000000000..5515cc26df
--- /dev/null
+++ b/package/source/manifest/ManifestWriter.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ManifestWriter.hxx"
+#include "ManifestExport.hxx"
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::registry;
+using namespace ::com::sun::star::packages;
+using namespace ::com::sun::star::xml::sax;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ManifestWriter::ManifestWriter( const Reference < XComponentContext > & xContext )
+: m_xContext ( xContext )
+{
+}
+ManifestWriter::~ManifestWriter()
+{
+}
+
+// XManifestWriter methods
+void SAL_CALL ManifestWriter::writeManifestSequence( const Reference< XOutputStream >& rStream, const Sequence< Sequence< PropertyValue > >& rSequence )
+{
+ Reference < XWriter > xSource = Writer::create( m_xContext );
+ xSource->setOutputStream ( rStream );
+ try {
+ ManifestExport( xSource, rSequence);
+ }
+ catch( SAXException& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( THROW_WHERE,
+ nullptr, anyEx );
+ }
+}
+
+OUString ManifestWriter::getImplementationName()
+{
+ return "com.sun.star.packages.manifest.comp.ManifestWriter";
+}
+
+sal_Bool SAL_CALL ManifestWriter::supportsService(OUString const & rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+Sequence < OUString > ManifestWriter::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.manifest.ManifestWriter" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+package_ManifestWriter_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ManifestWriter(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/manifest/ManifestWriter.hxx b/package/source/manifest/ManifestWriter.hxx
new file mode 100644
index 0000000000..ca08bb0e40
--- /dev/null
+++ b/package/source/manifest/ManifestWriter.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_MANIFEST_MANIFESTWRITER_HXX
+#define INCLUDED_PACKAGE_SOURCE_MANIFEST_MANIFESTWRITER_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/packages/manifest/XManifestWriter.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+namespace com::sun::star {
+ namespace lang { class XSingleServiceFactory; }
+ namespace uno { class XComponentContext; }
+}
+
+class ManifestWriter: public ::cppu::WeakImplHelper
+<
+ css::packages::manifest::XManifestWriter,
+ css::lang::XServiceInfo
+>
+{
+private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+public:
+ ManifestWriter( const css::uno::Reference< css::uno::XComponentContext > & xContext );
+ virtual ~ManifestWriter() override;
+
+ // XManifestWriter
+ virtual void SAL_CALL writeManifestSequence( const css::uno::Reference< css::io::XOutputStream >& rStream, const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& rSequence ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/disposelistener.cxx b/package/source/xstor/disposelistener.cxx
new file mode 100644
index 0000000000..b7ee409574
--- /dev/null
+++ b/package/source/xstor/disposelistener.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "disposelistener.hxx"
+#include "xstorage.hxx"
+
+using namespace ::com::sun::star;
+
+OChildDispListener_Impl::OChildDispListener_Impl( OStorage& aStorage )
+: m_pStorage( &aStorage )
+{}
+
+OChildDispListener_Impl::~OChildDispListener_Impl()
+{}
+
+void OChildDispListener_Impl::OwnerIsDisposed()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ m_pStorage = nullptr;
+}
+
+void SAL_CALL OChildDispListener_Impl::disposing( const lang::EventObject& Source )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ // ObjectIsDisposed must not contain any locking!
+ if ( m_pStorage && Source.Source.is() )
+ m_pStorage->ChildIsDisposed( Source.Source );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/disposelistener.hxx b/package/source/xstor/disposelistener.hxx
new file mode 100644
index 0000000000..b635b58ce3
--- /dev/null
+++ b/package/source/xstor/disposelistener.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_DISPOSELISTENER_HXX
+#define INCLUDED_PACKAGE_SOURCE_XSTOR_DISPOSELISTENER_HXX
+
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+
+class OStorage;
+class OChildDispListener_Impl : public ::cppu::WeakImplHelper<css::lang::XEventListener>
+{
+ std::mutex m_aMutex;
+ OStorage* m_pStorage;
+
+public:
+ explicit OChildDispListener_Impl(OStorage& aStorage);
+ virtual ~OChildDispListener_Impl() override;
+
+ void OwnerIsDisposed();
+
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/ocompinstream.cxx b/package/source/xstor/ocompinstream.cxx
new file mode 100644
index 0000000000..a7dd8ab219
--- /dev/null
+++ b/package/source/xstor/ocompinstream.cxx
@@ -0,0 +1,597 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ocompinstream.hxx"
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <utility>
+
+#include "owriteablestream.hxx"
+
+using namespace ::com::sun::star;
+
+OInputCompStream::OInputCompStream( OWriteStream_Impl& aImpl,
+ uno::Reference < io::XInputStream > xStream,
+ const uno::Sequence< beans::PropertyValue >& aProps,
+ sal_Int32 nStorageType )
+: m_pImpl( &aImpl )
+, m_xMutex( m_pImpl->m_xMutex )
+, m_xStream(std::move( xStream ))
+, m_aProperties( aProps )
+, m_bDisposed( false )
+, m_nStorageType( nStorageType )
+{
+ OSL_ENSURE( m_pImpl->m_xMutex.is(), "No mutex is provided!" );
+ if ( !m_pImpl->m_xMutex.is() )
+ throw uno::RuntimeException(); // just a disaster
+
+ assert(m_xStream.is());
+}
+
+OInputCompStream::OInputCompStream( uno::Reference < io::XInputStream > xStream,
+ const uno::Sequence< beans::PropertyValue >& aProps,
+ sal_Int32 nStorageType )
+: m_pImpl( nullptr )
+, m_xMutex( new comphelper::RefCountedMutex )
+, m_xStream(std::move( xStream ))
+, m_aProperties( aProps )
+, m_bDisposed( false )
+, m_nStorageType( nStorageType )
+{
+ assert(m_xStream.is());
+}
+
+OInputCompStream::~OInputCompStream()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_bDisposed )
+ {
+ osl_atomic_increment(&m_refCount);
+ dispose();
+ }
+}
+
+uno::Any SAL_CALL OInputCompStream::queryInterface( const uno::Type& rType )
+{
+ // common interfaces
+ uno::Any aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<io::XInputStream*> ( this )
+ , static_cast<io::XStream*> ( this )
+ , static_cast<lang::XComponent*> ( this )
+ , static_cast<beans::XPropertySet*> ( this )
+ , static_cast<embed::XExtendedStorageStream*> ( this ) );
+
+ if ( aReturn.hasValue() )
+ return aReturn ;
+
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<embed::XRelationshipAccess*> ( this ) );
+
+ if ( aReturn.hasValue() )
+ return aReturn ;
+ }
+
+ return OWeakObject::queryInterface( rType );
+}
+
+sal_Int32 SAL_CALL OInputCompStream::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ return m_xStream->readBytes( aData, nBytesToRead );
+}
+
+sal_Int32 SAL_CALL OInputCompStream::readSomeBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ return m_xStream->readSomeBytes( aData, nMaxBytesToRead );
+
+}
+
+void SAL_CALL OInputCompStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ m_xStream->skipBytes( nBytesToSkip );
+
+}
+
+sal_Int32 SAL_CALL OInputCompStream::available( )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ return m_xStream->available();
+
+}
+
+void SAL_CALL OInputCompStream::closeInput( )
+{
+ dispose();
+}
+
+uno::Reference< io::XInputStream > SAL_CALL OInputCompStream::getInputStream()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ return this;
+}
+
+uno::Reference< io::XOutputStream > SAL_CALL OInputCompStream::getOutputStream()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ return uno::Reference< io::XOutputStream >();
+}
+
+void OInputCompStream::InternalDispose()
+{
+ // can be called only by OWriteStream_Impl
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ return;
+
+ // the source object is also a kind of locker for the current object
+ // since the listeners could dispose the object while being notified
+ lang::EventObject aSource( getXWeak() );
+
+ if ( m_pInterfaceContainer )
+ m_pInterfaceContainer->disposeAndClear( aSource );
+
+ try
+ {
+ m_xStream->closeInput();
+ }
+ catch( uno::Exception& )
+ {}
+
+ m_pImpl = nullptr;
+ m_bDisposed = true;
+}
+
+void SAL_CALL OInputCompStream::dispose( )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ return;
+
+ if ( m_pInterfaceContainer )
+ {
+ lang::EventObject aSource( getXWeak() );
+ m_pInterfaceContainer->disposeAndClear( aSource );
+ }
+
+ m_xStream->closeInput();
+
+ if ( m_pImpl )
+ {
+ m_pImpl->InputStreamDisposed( this );
+ m_pImpl = nullptr;
+ }
+
+ m_bDisposed = true;
+}
+
+void SAL_CALL OInputCompStream::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_pInterfaceContainer )
+ m_pInterfaceContainer.reset( new ::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>( m_xMutex->GetMutex() ) );
+
+ m_pInterfaceContainer->addInterface( xListener );
+}
+
+void SAL_CALL OInputCompStream::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_pInterfaceContainer )
+ m_pInterfaceContainer->removeInterface( xListener );
+}
+
+sal_Bool SAL_CALL OInputCompStream::hasByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ try
+ {
+ getRelationshipByID( sID );
+ return true;
+ }
+ catch( container::NoSuchElementException& )
+ {}
+
+ return false;
+}
+
+namespace
+{
+
+const beans::StringPair* lcl_findPairByName(const uno::Sequence<beans::StringPair>& rSeq, const OUString& rName)
+{
+ return std::find_if(rSeq.begin(), rSeq.end(),
+ [&rName](const beans::StringPair& rPair) { return rPair.First == rName; });
+}
+
+}
+
+OUString SAL_CALL OInputCompStream::getTargetByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID );
+ auto pRel = lcl_findPairByName(aSeq, "Target");
+ if (pRel != aSeq.end())
+ return pRel->Second;
+
+ return OUString();
+}
+
+OUString SAL_CALL OInputCompStream::getTypeByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID );
+ auto pRel = lcl_findPairByName(aSeq, "Type");
+ if (pRel != aSeq.end())
+ return pRel->Second;
+
+ return OUString();
+}
+
+uno::Sequence< beans::StringPair > SAL_CALL OInputCompStream::getRelationshipByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ const beans::StringPair aIDRel("Id", sID);
+ auto pRel = std::find_if(aSeq.begin(), aSeq.end(),
+ [&aIDRel](const uno::Sequence<beans::StringPair>& rRel){
+ return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); });
+ if (pRel != aSeq.end())
+ return *pRel;
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OInputCompStream::getRelationshipsByType( const OUString& sType )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ const beans::StringPair aTypeRel("Type", sType);
+ std::vector< uno::Sequence<beans::StringPair> > aResult;
+ aResult.reserve(aSeq.getLength());
+
+ std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResult),
+ [&aTypeRel](const uno::Sequence<beans::StringPair>& rRel) {
+ return std::find(rRel.begin(), rRel.end(), aTypeRel) != rRel.end(); });
+
+ return comphelper::containerToSequence(aResult);
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OInputCompStream::getAllRelationships()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ // TODO/LATER: in future the information could be taken directly from m_pImpl when possible
+ auto pProp = std::find_if(std::cbegin(m_aProperties), std::cend(m_aProperties),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "RelationsInfo"; });
+ if (pProp != std::cend(m_aProperties))
+ {
+ uno::Sequence< uno::Sequence< beans::StringPair > > aResult;
+ if (pProp->Value >>= aResult)
+ return aResult;
+ }
+
+ throw io::IOException("relations info could not be read"); // the relations info could not be read
+}
+
+void SAL_CALL OInputCompStream::insertRelationshipByID( const OUString& /*sID*/, const uno::Sequence< beans::StringPair >& /*aEntry*/, sal_Bool /*bReplace*/ )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ throw io::IOException(); // TODO: Access denied
+}
+
+void SAL_CALL OInputCompStream::removeRelationshipByID( const OUString& /*sID*/ )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ throw io::IOException(); // TODO: Access denied
+}
+
+void SAL_CALL OInputCompStream::insertRelationships( const uno::Sequence< uno::Sequence< beans::StringPair > >& /*aEntries*/, sal_Bool /*bReplace*/ )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ throw io::IOException(); // TODO: Access denied
+}
+
+void SAL_CALL OInputCompStream::clearRelationships()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ throw io::IOException(); // TODO: Access denied
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL OInputCompStream::getPropertySetInfo()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ //TODO:
+ return uno::Reference< beans::XPropertySetInfo >();
+}
+
+void SAL_CALL OInputCompStream::setPropertyValue( const OUString& aPropertyName, const uno::Any& /*aValue*/ )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ // all the provided properties are accessible
+ if (std::any_of(std::cbegin(m_aProperties), std::cend(m_aProperties),
+ [&aPropertyName](const beans::PropertyValue& rProp) { return rProp.Name == aPropertyName; }))
+ throw beans::PropertyVetoException(); // TODO
+
+ throw beans::UnknownPropertyException(aPropertyName); // TODO
+}
+
+uno::Any SAL_CALL OInputCompStream::getPropertyValue( const OUString& aProp )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ OUString aPropertyName;
+ if ( aProp == "IsEncrypted" )
+ aPropertyName = "Encrypted";
+ else
+ aPropertyName = aProp;
+
+ if ( aPropertyName == "RelationsInfo" )
+ throw beans::UnknownPropertyException(aPropertyName); // TODO
+
+ // all the provided properties are accessible
+ auto pProp = std::find_if(std::cbegin(m_aProperties), std::cend(m_aProperties),
+ [&aPropertyName](const beans::PropertyValue& rProp) { return rProp.Name == aPropertyName; });
+ if (pProp != std::cend(m_aProperties))
+ return pProp->Value;
+
+ throw beans::UnknownPropertyException(aPropertyName); // TODO
+}
+
+void SAL_CALL OInputCompStream::addPropertyChangeListener(
+ const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ //TODO:
+}
+
+void SAL_CALL OInputCompStream::removePropertyChangeListener(
+ const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ //TODO:
+}
+
+void SAL_CALL OInputCompStream::addVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ //TODO:
+}
+
+void SAL_CALL OInputCompStream::removeVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ //TODO:
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/ocompinstream.hxx b/package/source/xstor/ocompinstream.hxx
new file mode 100644
index 0000000000..effbc4be2f
--- /dev/null
+++ b/package/source/xstor/ocompinstream.hxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_OCOMPINSTREAM_HXX
+#define INCLUDED_PACKAGE_SOURCE_XSTOR_OCOMPINSTREAM_HXX
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/XExtendedStorageStream.hpp>
+#include <com/sun/star/embed/XRelationshipAccess.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/refcountedmutex.hxx>
+#include <rtl/ref.hxx>
+#include <memory>
+
+
+struct OWriteStream_Impl;
+
+class OInputCompStream : public cppu::WeakImplHelper < css::io::XInputStream
+ ,css::embed::XExtendedStorageStream
+ ,css::embed::XRelationshipAccess
+ ,css::beans::XPropertySet >
+{
+protected:
+ OWriteStream_Impl* m_pImpl;
+ rtl::Reference<comphelper::RefCountedMutex> m_xMutex;
+ css::uno::Reference < css::io::XInputStream > m_xStream;
+ std::unique_ptr<::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>> m_pInterfaceContainer;
+ css::uno::Sequence < css::beans::PropertyValue > m_aProperties;
+ bool m_bDisposed;
+ sal_Int32 m_nStorageType;
+
+public:
+ OInputCompStream( OWriteStream_Impl& pImpl,
+ css::uno::Reference< css::io::XInputStream > xStream,
+ const css::uno::Sequence< css::beans::PropertyValue >& aProps,
+ sal_Int32 nStorageType );
+
+ OInputCompStream( css::uno::Reference< css::io::XInputStream > xStream,
+ const css::uno::Sequence< css::beans::PropertyValue >& aProps,
+ sal_Int32 nStorageType );
+
+ virtual ~OInputCompStream() override;
+
+ void InternalDispose();
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override;
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+
+ //XStream
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getInputStream( ) override;
+ virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL getOutputStream( ) override;
+
+ //XComponent
+ virtual void SAL_CALL dispose( ) override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ //XRelationshipAccess
+ virtual sal_Bool SAL_CALL hasByID( const OUString& sID ) override;
+ virtual OUString SAL_CALL getTargetByID( const OUString& sID ) override;
+ virtual OUString SAL_CALL getTypeByID( const OUString& sID ) override;
+ virtual css::uno::Sequence< css::beans::StringPair > SAL_CALL getRelationshipByID( const OUString& sID ) override;
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getRelationshipsByType( const OUString& sType ) override;
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getAllRelationships( ) override;
+ virtual void SAL_CALL insertRelationshipByID( const OUString& sID, const css::uno::Sequence< css::beans::StringPair >& aEntry, sal_Bool bReplace ) override;
+ virtual void SAL_CALL removeRelationshipByID( const OUString& sID ) override;
+ virtual void SAL_CALL insertRelationships( const css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > >& aEntries, sal_Bool bReplace ) override;
+ virtual void SAL_CALL clearRelationships( ) override;
+
+ //XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/ohierarchyholder.cxx b/package/source/xstor/ohierarchyholder.cxx
new file mode 100644
index 0000000000..d9b5b13743
--- /dev/null
+++ b/package/source/xstor/ohierarchyholder.cxx
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XHierarchicalStorageAccess2.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/XTransactionBroadcaster.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <o3tl/string_view.hxx>
+
+#include "ohierarchyholder.hxx"
+
+using namespace ::com::sun::star;
+
+// OHierarchyHolder_Impl
+
+uno::Reference< embed::XExtendedStorageStream > OHierarchyHolder_Impl::GetStreamHierarchically( sal_Int32 nStorageMode, std::vector<OUString>& aListPath, sal_Int32 nStreamMode, const ::comphelper::SequenceAsHashMap& aEncryptionData )
+{
+ uno::Reference< embed::XStorage > xOwnStor( m_xWeakOwnStorage.get(), uno::UNO_QUERY_THROW );
+
+ if ( !( nStorageMode & embed::ElementModes::WRITE ) && ( nStreamMode & embed::ElementModes::WRITE ) )
+ throw io::IOException("invalid storage/stream mode combo");
+
+ uno::Reference< embed::XExtendedStorageStream > xResult =
+ m_xChild->GetStreamHierarchically( nStorageMode, aListPath, nStreamMode, aEncryptionData );
+ if ( !xResult.is() )
+ throw uno::RuntimeException();
+
+ return xResult;
+}
+
+void OHierarchyHolder_Impl::RemoveStreamHierarchically( std::vector<OUString>& aListPath )
+{
+ uno::Reference< embed::XStorage > xOwnStor( m_xWeakOwnStorage.get(), uno::UNO_QUERY_THROW );
+
+ m_xChild->RemoveStreamHierarchically( aListPath );
+}
+
+// static
+std::vector<OUString> OHierarchyHolder_Impl::GetListPathFromString( std::u16string_view aPath )
+{
+ std::vector<OUString> aResult;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aName( o3tl::getToken(aPath, 0, '/', nIndex ) );
+ if ( aName.isEmpty() )
+ throw lang::IllegalArgumentException();
+
+ aResult.push_back( aName );
+ }
+ while ( nIndex >= 0 );
+
+ return aResult;
+}
+
+// OHierarchyElement_Impl
+
+uno::Reference< embed::XExtendedStorageStream > OHierarchyElement_Impl::GetStreamHierarchically( sal_Int32 nStorageMode, std::vector<OUString>& aListPath, sal_Int32 nStreamMode, const ::comphelper::SequenceAsHashMap& aEncryptionData )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !( nStorageMode & embed::ElementModes::WRITE ) && ( nStreamMode & embed::ElementModes::WRITE ) )
+ throw io::IOException("invalid storage/stream mode combo");
+
+ if ( aListPath.empty() )
+ throw uno::RuntimeException();
+
+ OUString aNextName = *(aListPath.begin());
+ aListPath.erase( aListPath.begin() );
+
+ uno::Reference< embed::XExtendedStorageStream > xResult;
+
+ uno::Reference< embed::XStorage > xOwnStor = m_xOwnStorage.is() ? m_xOwnStorage
+ : uno::Reference< embed::XStorage >( m_xWeakOwnStorage.get(), uno::UNO_QUERY_THROW );
+
+ if ( aListPath.empty() )
+ {
+ if ( aEncryptionData.empty() )
+ {
+ uno::Reference< embed::XHierarchicalStorageAccess > xHStorage( xOwnStor, uno::UNO_QUERY_THROW );
+ xResult = xHStorage->openStreamElementByHierarchicalName( aNextName, nStreamMode );
+ }
+ else
+ {
+ uno::Reference< embed::XHierarchicalStorageAccess2 > xHStorage( xOwnStor, uno::UNO_QUERY_THROW );
+ xResult = xHStorage->openEncryptedStreamByHierarchicalName( aNextName, nStreamMode, aEncryptionData.getAsConstNamedValueList() );
+ }
+
+ uno::Reference< embed::XTransactedObject > xTransact( xResult, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ {
+ // the existence of the transacted object means that the stream is opened for writing also
+ // so the whole chain must be committed
+ uno::Reference< embed::XTransactionBroadcaster > xTrBroadcast( xTransact, uno::UNO_QUERY_THROW );
+ xTrBroadcast->addTransactionListener( static_cast< embed::XTransactionListener* >( this ) );
+ }
+ else
+ {
+ uno::Reference< lang::XComponent > xStreamComp( xResult, uno::UNO_QUERY_THROW );
+ xStreamComp->addEventListener( static_cast< lang::XEventListener* >( this ) );
+ }
+
+ m_aOpenStreams.emplace_back( xResult );
+ }
+ else
+ {
+ bool bNewElement = false;
+ ::rtl::Reference< OHierarchyElement_Impl > aElement;
+ OHierarchyElementList_Impl::iterator aIter = m_aChildren.find( aNextName );
+ if ( aIter != m_aChildren.end() )
+ aElement = aIter->second;
+
+ if ( !aElement.is() )
+ {
+ bNewElement = true;
+ uno::Reference< embed::XStorage > xChildStorage = xOwnStor->openStorageElement( aNextName, nStorageMode );
+ if ( !xChildStorage.is() )
+ throw uno::RuntimeException();
+
+ aElement = new OHierarchyElement_Impl( xChildStorage );
+ }
+
+ xResult = aElement->GetStreamHierarchically( nStorageMode, aListPath, nStreamMode, aEncryptionData );
+ if ( !xResult.is() )
+ throw uno::RuntimeException();
+
+ if ( bNewElement )
+ {
+ m_aChildren[aNextName] = aElement;
+ aElement->SetParent( this );
+ }
+ }
+
+ // the subelement was opened successfully, remember the storage to let it be locked
+ m_xOwnStorage = xOwnStor;
+
+ return xResult;
+}
+
+void OHierarchyElement_Impl::RemoveStreamHierarchically( std::vector<OUString>& aListPath )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( aListPath.empty() )
+ throw uno::RuntimeException();
+
+ OUString aNextName = *(aListPath.begin());
+ aListPath.erase( aListPath.begin() );
+
+ uno::Reference< embed::XStorage > xOwnStor = m_xOwnStorage.is() ? m_xOwnStorage
+ : uno::Reference< embed::XStorage >( m_xWeakOwnStorage.get(), uno::UNO_QUERY_THROW );
+
+ if ( aListPath.empty() )
+ {
+ xOwnStor->removeElement( aNextName );
+ }
+ else
+ {
+ ::rtl::Reference< OHierarchyElement_Impl > aElement;
+ OHierarchyElementList_Impl::iterator aIter = m_aChildren.find( aNextName );
+ if ( aIter != m_aChildren.end() )
+ aElement = aIter->second;
+
+ if ( !aElement.is() )
+ {
+ uno::Reference< embed::XStorage > xChildStorage = xOwnStor->openStorageElement( aNextName,
+ embed::ElementModes::READWRITE );
+ if ( !xChildStorage.is() )
+ throw uno::RuntimeException();
+
+ aElement = new OHierarchyElement_Impl( xChildStorage );
+ }
+
+ aElement->RemoveStreamHierarchically( aListPath );
+ }
+
+ uno::Reference< embed::XTransactedObject > xTransact( xOwnStor, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ xTransact->commit();
+
+ TestForClosing();
+}
+
+void OHierarchyElement_Impl::Commit()
+{
+ ::rtl::Reference< OHierarchyElement_Impl > xKeepAlive( this );
+ ::rtl::Reference< OHierarchyElement_Impl > aParent;
+ uno::Reference< embed::XStorage > xOwnStor;
+
+ {
+ std::unique_lock aGuard( m_aMutex );
+ aParent = m_rParent;
+ xOwnStor = m_xOwnStorage;
+ }
+
+ if ( xOwnStor.is() )
+ {
+ uno::Reference< embed::XTransactedObject > xTransact( xOwnStor, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+ if ( aParent.is() )
+ aParent->Commit();
+ }
+}
+
+void OHierarchyElement_Impl::TestForClosing()
+{
+ ::rtl::Reference< OHierarchyElement_Impl > xKeepAlive( this );
+ {
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_aOpenStreams.empty() && m_aChildren.empty() )
+ {
+ if ( m_rParent.is() )
+ {
+ // only the root storage should not be disposed, other storages can be disposed
+ if ( m_xOwnStorage.is() )
+ {
+ try
+ {
+ m_xOwnStorage->dispose();
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ m_rParent->RemoveElement( this );
+ }
+
+ m_xOwnStorage.clear();
+ }
+ }
+}
+
+void SAL_CALL OHierarchyElement_Impl::disposing( const lang::EventObject& Source )
+{
+ try
+ {
+ {
+ std::unique_lock aGuard(m_aMutex);
+ uno::Reference< embed::XExtendedStorageStream > xStream(Source.Source, uno::UNO_QUERY);
+
+ std::erase_if(m_aOpenStreams,
+ [&xStream](const OWeakStorRefVector_Impl::value_type& rxStorage) {
+ return !rxStorage.get().is() || rxStorage.get() == xStream; });
+ }
+
+ TestForClosing();
+ }
+ catch( uno::Exception& ex )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException( ex.Message,
+ nullptr, anyEx ); // no exception must happen here, usually an exception means disaster
+ }
+}
+
+void OHierarchyElement_Impl::RemoveElement( const ::rtl::Reference< OHierarchyElement_Impl >& aRef )
+{
+ {
+ std::unique_lock aGuard( m_aMutex );
+ OHierarchyElementList_Impl::iterator aIter = m_aChildren.begin();
+ while (aIter != m_aChildren.end())
+ {
+ if (aIter->second == aRef )
+ aIter = m_aChildren.erase(aIter);
+ else
+ ++aIter;
+ }
+ }
+
+ TestForClosing();
+}
+
+// XTransactionListener
+void SAL_CALL OHierarchyElement_Impl::preCommit( const css::lang::EventObject& /*aEvent*/ )
+{
+}
+
+void SAL_CALL OHierarchyElement_Impl::commited( const css::lang::EventObject& /*aEvent*/ )
+{
+ try
+ {
+ Commit();
+ }
+ catch( const uno::Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "Can not commit storage sequence!",
+ uno::Reference< uno::XInterface >(),
+ anyEx );
+ }
+}
+
+void SAL_CALL OHierarchyElement_Impl::preRevert( const css::lang::EventObject& /*aEvent*/ )
+{
+}
+
+void SAL_CALL OHierarchyElement_Impl::reverted( const css::lang::EventObject& /*aEvent*/ )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/ohierarchyholder.hxx b/package/source/xstor/ohierarchyholder.hxx
new file mode 100644
index 0000000000..c3ceddabd1
--- /dev/null
+++ b/package/source/xstor/ohierarchyholder.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_OHIERARCHYHOLDER_HXX
+#define INCLUDED_PACKAGE_SOURCE_XSTOR_OHIERARCHYHOLDER_HXX
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/XTransactionListener.hpp>
+#include <com/sun/star/embed/XExtendedStorageStream.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+
+#include <comphelper/sequenceashashmap.hxx>
+
+#include <rtl/ref.hxx>
+#include <mutex>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+class OHierarchyElement_Impl;
+
+typedef std::unordered_map< OUString,
+ ::rtl::Reference< OHierarchyElement_Impl > > OHierarchyElementList_Impl;
+
+typedef ::std::vector< css::uno::WeakReference< css::embed::XExtendedStorageStream > >
+ OWeakStorRefVector_Impl;
+
+class OHierarchyElement_Impl : public cppu::WeakImplHelper< css::embed::XTransactionListener >
+{
+ std::mutex m_aMutex;
+
+ ::rtl::Reference< OHierarchyElement_Impl > m_rParent;
+ css::uno::Reference< css::embed::XStorage > m_xOwnStorage;
+ css::uno::WeakReference< css::embed::XStorage > m_xWeakOwnStorage;
+
+ OHierarchyElementList_Impl m_aChildren;
+
+ OWeakStorRefVector_Impl m_aOpenStreams;
+
+public:
+ explicit OHierarchyElement_Impl( css::uno::Reference< css::embed::XStorage > xStorage )
+ : m_xOwnStorage(std::move( xStorage ))
+ {}
+
+ explicit OHierarchyElement_Impl( css::uno::WeakReference< css::embed::XStorage > xWeakStorage )
+ : m_xWeakOwnStorage(std::move( xWeakStorage ))
+ {}
+
+ void Commit();
+
+ void SetParent( const ::rtl::Reference< OHierarchyElement_Impl >& rParent ) { m_rParent = rParent; }
+
+ void TestForClosing();
+
+ void RemoveElement( const ::rtl::Reference< OHierarchyElement_Impl >& aRef );
+
+ css::uno::Reference< css::embed::XExtendedStorageStream >
+ GetStreamHierarchically( sal_Int32 nStorageMode,
+ std::vector<OUString>& aPath,
+ sal_Int32 nStreamMode,
+ const ::comphelper::SequenceAsHashMap& aEncryptionData );
+
+ void RemoveStreamHierarchically( std::vector<OUString>& aListPath );
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XTransactionListener
+ virtual void SAL_CALL preCommit( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL commited( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL preRevert( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL reverted( const css::lang::EventObject& aEvent ) override;
+
+};
+
+class OHierarchyHolder_Impl : public ::cppu::OWeakObject
+{
+ css::uno::WeakReference< css::embed::XStorage > m_xWeakOwnStorage;
+ ::rtl::Reference< OHierarchyElement_Impl > m_xChild;
+public:
+ explicit OHierarchyHolder_Impl( const css::uno::Reference< css::embed::XStorage >& xOwnStorage )
+ : m_xWeakOwnStorage( xOwnStorage )
+ , m_xChild( new OHierarchyElement_Impl( css::uno::WeakReference< css::embed::XStorage >( xOwnStorage ) ) )
+ {}
+
+ static std::vector<OUString> GetListPathFromString( std::u16string_view aPath );
+
+ css::uno::Reference< css::embed::XExtendedStorageStream >
+ GetStreamHierarchically( sal_Int32 nStorageMode,
+ std::vector<OUString>& aListPath,
+ sal_Int32 nStreamMode,
+ const ::comphelper::SequenceAsHashMap& aEncryptionData = ::comphelper::SequenceAsHashMap() );
+
+ void RemoveStreamHierarchically( std::vector<OUString>& aListPath );
+};
+
+#endif // _OHIERARCHYHOLDER
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/oseekinstream.cxx b/package/source/xstor/oseekinstream.cxx
new file mode 100644
index 0000000000..f5e3d644d6
--- /dev/null
+++ b/package/source/xstor/oseekinstream.cxx
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include "oseekinstream.hxx"
+#include "owriteablestream.hxx"
+
+using namespace ::com::sun::star;
+
+OInputSeekStream::OInputSeekStream( OWriteStream_Impl& pImpl,
+ uno::Reference < io::XInputStream > const & xStream,
+ const uno::Sequence< beans::PropertyValue >& aProps,
+ sal_Int32 nStorageType )
+: OInputCompStream( pImpl, xStream, aProps, nStorageType )
+{
+ m_xSeekable.set( m_xStream, uno::UNO_QUERY );
+ OSL_ENSURE( m_xSeekable.is(), "No seeking support!" );
+}
+
+OInputSeekStream::OInputSeekStream( uno::Reference < io::XInputStream > const & xStream,
+ const uno::Sequence< beans::PropertyValue >& aProps,
+ sal_Int32 nStorageType )
+: OInputCompStream( xStream, aProps, nStorageType )
+{
+ m_xSeekable.set( m_xStream, uno::UNO_QUERY );
+ OSL_ENSURE( m_xSeekable.is(), "No seeking support!" );
+}
+
+OInputSeekStream::~OInputSeekStream()
+{
+}
+
+uno::Sequence< uno::Type > SAL_CALL OInputSeekStream::getTypes()
+{
+ static cppu::OTypeCollection aTypeCollection(cppu::UnoType<io::XSeekable>::get(),
+ OInputCompStream::getTypes());
+
+ return aTypeCollection.getTypes();
+}
+
+uno::Any SAL_CALL OInputSeekStream::queryInterface( const uno::Type& rType )
+{
+ // Attention:
+ // Don't use mutex or guard in this method!!! Is a method of XInterface.
+
+ uno::Any aReturn( ::cppu::queryInterface( rType,
+ static_cast< io::XSeekable* >( this ) ) );
+
+ if ( aReturn.hasValue() )
+ {
+ return aReturn ;
+ }
+
+ return OInputCompStream::queryInterface( rType ) ;
+}
+
+void SAL_CALL OInputSeekStream::acquire()
+ noexcept
+{
+ OInputCompStream::acquire();
+}
+
+void SAL_CALL OInputSeekStream::release()
+ noexcept
+{
+ OInputCompStream::release();
+}
+
+void SAL_CALL OInputSeekStream::seek( sal_Int64 location )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xSeekable.is() )
+ {
+ SAL_INFO("package.xstor", "No seekable!");
+ throw uno::RuntimeException();
+ }
+
+ m_xSeekable->seek( location );
+}
+
+sal_Int64 SAL_CALL OInputSeekStream::getPosition()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xSeekable.is() )
+ {
+ SAL_INFO("package.xstor", "No seekable!");
+ throw uno::RuntimeException();
+ }
+
+ return m_xSeekable->getPosition();
+}
+
+sal_Int64 SAL_CALL OInputSeekStream::getLength()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_bDisposed )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xSeekable.is() )
+ {
+ SAL_INFO("package.xstor", "No seekable!");
+ throw uno::RuntimeException();
+ }
+
+ return m_xSeekable->getLength();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/oseekinstream.hxx b/package/source/xstor/oseekinstream.hxx
new file mode 100644
index 0000000000..a77289e9a3
--- /dev/null
+++ b/package/source/xstor/oseekinstream.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_OSEEKINSTREAM_HXX
+#define INCLUDED_PACKAGE_SOURCE_XSTOR_OSEEKINSTREAM_HXX
+
+#include <com/sun/star/io/XSeekable.hpp>
+
+#include "ocompinstream.hxx"
+
+class OInputSeekStream final : public OInputCompStream
+ , public css::io::XSeekable
+{
+ css::uno::Reference < css::io::XSeekable > m_xSeekable;
+
+public:
+ OInputSeekStream( OWriteStream_Impl& pImpl,
+ css::uno::Reference < css::io::XInputStream > const & xStream,
+ const css::uno::Sequence< css::beans::PropertyValue >& aProps,
+ sal_Int32 nStorageType );
+
+ OInputSeekStream( css::uno::Reference < css::io::XInputStream > const & xStream,
+ const css::uno::Sequence< css::beans::PropertyValue >& aProps,
+ sal_Int32 nStorageType );
+
+ virtual ~OInputSeekStream() override;
+
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ //XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+ virtual sal_Int64 SAL_CALL getPosition() override;
+ virtual sal_Int64 SAL_CALL getLength() override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/owriteablestream.cxx b/package/source/xstor/owriteablestream.cxx
new file mode 100644
index 0000000000..68bf5d1688
--- /dev/null
+++ b/package/source/xstor/owriteablestream.cxx
@@ -0,0 +1,3189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <memory>
+#include <sal/log.hxx>
+
+#include <com/sun/star/packages/NoEncryptionException.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/diagnose.h>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
+#include <comphelper/refcountedmutex.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <comphelper/diagnose_ex.hxx>
+
+#include <PackageConstants.hxx>
+#include <utility>
+
+#include "selfterminatefilestream.hxx"
+#include "owriteablestream.hxx"
+#include "oseekinstream.hxx"
+#include "xstorage.hxx"
+
+// since the copying uses 32000 blocks usually, it makes sense to have a smaller size
+#define MAX_STORCACHE_SIZE 30000
+
+using namespace ::com::sun::star;
+
+namespace package
+{
+
+static void CopyInputToOutput(
+ const css::uno::Reference< css::io::XInputStream >& xInput,
+ SvStream& rOutput )
+{
+ static const sal_Int32 nConstBufferSize = 32000;
+
+ if (auto pByteReader = dynamic_cast< comphelper::ByteReader* >( xInput.get() ))
+ {
+ sal_Int32 nRead;
+ sal_Int8 aTempBuf[ nConstBufferSize ];
+ do
+ {
+ nRead = pByteReader->readSomeBytes ( aTempBuf, nConstBufferSize );
+ rOutput.WriteBytes ( aTempBuf, nRead );
+ }
+ while ( nRead == nConstBufferSize );
+ }
+ else
+ {
+ sal_Int32 nRead;
+ uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize );
+
+ do
+ {
+ nRead = xInput->readBytes ( aSequence, nConstBufferSize );
+ rOutput.WriteBytes ( aSequence.getConstArray(), nRead );
+ }
+ while ( nRead == nConstBufferSize );
+ }
+}
+
+bool PackageEncryptionDataLessOrEqual( const ::comphelper::SequenceAsHashMap& aHash1, const ::comphelper::SequenceAsHashMap& aHash2 )
+{
+ // tdf#93389: aHash2 may contain more than in aHash1, if it contains also data for other package
+ // formats (as in case of autorecovery)
+ bool bResult = !aHash1.empty() && aHash1.size() <= aHash2.size();
+ for ( ::comphelper::SequenceAsHashMap::const_iterator aIter = aHash1.begin();
+ bResult && aIter != aHash1.end();
+ ++aIter )
+ {
+ uno::Sequence< sal_Int8 > aKey1;
+ bResult = ( ( aIter->second >>= aKey1 ) && aKey1.hasElements() );
+ if ( bResult )
+ {
+ const uno::Sequence< sal_Int8 > aKey2 = aHash2.getUnpackedValueOrDefault( aIter->first.maString, uno::Sequence< sal_Int8 >() );
+ bResult = aKey1.getLength() == aKey2.getLength()
+ && std::equal(std::cbegin(aKey1), std::cend(aKey1), aKey2.begin(), aKey2.end());
+ }
+ }
+
+ return bResult;
+}
+
+} // namespace package
+
+namespace
+{
+
+void SetEncryptionKeyProperty_Impl( const uno::Reference< beans::XPropertySet >& xPropertySet,
+ const uno::Sequence< beans::NamedValue >& aKey )
+{
+ SAL_WARN_IF( !xPropertySet.is(), "package.xstor", "No property set is provided!" );
+ if ( !xPropertySet.is() )
+ throw uno::RuntimeException();
+
+ try {
+ xPropertySet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY, uno::Any( aKey ) );
+ }
+ catch ( const uno::Exception& ex )
+ {
+ TOOLS_WARN_EXCEPTION( "package.xstor", "Can't write encryption related properties");
+ throw io::IOException(ex.Message); // TODO
+ }
+}
+
+uno::Any GetEncryptionKeyProperty_Impl( const uno::Reference< beans::XPropertySet >& xPropertySet )
+{
+ SAL_WARN_IF( !xPropertySet.is(), "package.xstor", "No property set is provided!" );
+ if ( !xPropertySet.is() )
+ throw uno::RuntimeException();
+
+ try {
+ return xPropertySet->getPropertyValue(STORAGE_ENCRYPTION_KEYS_PROPERTY);
+ }
+ catch ( const uno::Exception& ex )
+ {
+ TOOLS_WARN_EXCEPTION( "package.xstor", "Can't get encryption related properties");
+ throw io::IOException(ex.Message); // TODO
+ }
+}
+
+bool SequencesEqual( const uno::Sequence< sal_Int8 >& aSequence1, const uno::Sequence< sal_Int8 >& aSequence2 )
+{
+ return aSequence1.getLength() == aSequence2.getLength()
+ && std::equal(aSequence1.begin(), aSequence1.end(), aSequence2.begin(), aSequence2.end());
+}
+
+bool SequencesEqual( const uno::Sequence< beans::NamedValue >& aSequence1, const uno::Sequence< beans::NamedValue >& aSequence2 )
+{
+ if ( aSequence1.getLength() != aSequence2.getLength() )
+ return false;
+
+ for ( const auto& rProp1 : aSequence1 )
+ {
+ bool bHasMember = false;
+ uno::Sequence< sal_Int8 > aMember1;
+ sal_Int32 nMember1 = 0;
+ if ( rProp1.Value >>= aMember1 )
+ {
+ for ( const auto& rProp2 : aSequence2 )
+ {
+ if ( rProp1.Name == rProp2.Name )
+ {
+ bHasMember = true;
+
+ uno::Sequence< sal_Int8 > aMember2;
+ if ( !( rProp2.Value >>= aMember2 ) || !SequencesEqual( aMember1, aMember2 ) )
+ return false;
+ }
+ }
+ }
+ else if ( rProp1.Value >>= nMember1 )
+ {
+ for ( const auto& rProp2 : aSequence2 )
+ {
+ if ( rProp1.Name == rProp2.Name )
+ {
+ bHasMember = true;
+
+ sal_Int32 nMember2 = 0;
+ if ( !( rProp2.Value >>= nMember2 ) || nMember1 != nMember2 )
+ return false;
+ }
+ }
+ }
+ else
+ return false;
+
+ if ( !bHasMember )
+ return false;
+ }
+
+ return true;
+}
+
+uno::Reference< io::XStream > CreateMemoryStream( const uno::Reference< uno::XComponentContext >& rContext )
+{
+ static constexpr OUStringLiteral sName(u"com.sun.star.comp.MemoryStream");
+ return uno::Reference< io::XStream >(
+ rContext->getServiceManager()->createInstanceWithContext(sName, rContext),
+ uno::UNO_QUERY_THROW);
+}
+
+const beans::StringPair* lcl_findPairByName(const uno::Sequence<beans::StringPair>& rSeq, const OUString& rName)
+{
+ return std::find_if(rSeq.begin(), rSeq.end(),
+ [&rName](const beans::StringPair& rPair) { return rPair.First == rName; });
+}
+
+} // anonymous namespace
+
+OWriteStream_Impl::OWriteStream_Impl( OStorage_Impl* pParent,
+ const uno::Reference< packages::XDataSinkEncrSupport >& xPackageStream,
+ const uno::Reference< lang::XSingleServiceFactory >& xPackage,
+ uno::Reference< uno::XComponentContext > xContext,
+ bool bForceEncrypted,
+ sal_Int32 nStorageType,
+ bool bDefaultCompress,
+ uno::Reference< io::XInputStream > xRelInfoStream )
+: m_xMutex( new comphelper::RefCountedMutex )
+, m_pAntiImpl( nullptr )
+, m_bHasDataToFlush( false )
+, m_bFlushed( false )
+, m_xPackageStream( xPackageStream )
+, m_xContext(std::move( xContext ))
+, m_pParent( pParent )
+, m_bForceEncrypted( bForceEncrypted )
+, m_bUseCommonEncryption( !bForceEncrypted && nStorageType == embed::StorageFormats::PACKAGE )
+, m_bHasCachedEncryptionData( false )
+, m_bCompressedSetExplicit( !bDefaultCompress )
+, m_xPackage( xPackage )
+, m_bHasInsertedStreamOptimization( false )
+, m_nStorageType( nStorageType )
+, m_xOrigRelInfoStream(std::move( xRelInfoStream ))
+, m_bOrigRelInfoBroken( false )
+, m_nRelInfoStatus( RELINFO_NO_INIT )
+, m_nRelId( 1 )
+{
+ SAL_WARN_IF( !xPackageStream.is(), "package.xstor", "No package stream is provided!" );
+ SAL_WARN_IF( !xPackage.is(), "package.xstor", "No package component is provided!" );
+ SAL_WARN_IF( !m_xContext.is(), "package.xstor", "No package stream is provided!" );
+ OSL_ENSURE( pParent, "No parent storage is provided!" );
+ OSL_ENSURE( m_nStorageType == embed::StorageFormats::OFOPXML || !m_xOrigRelInfoStream.is(), "The Relations info makes sense only for OFOPXML format!" );
+}
+
+OWriteStream_Impl::~OWriteStream_Impl()
+{
+ DisposeWrappers();
+
+ m_oTempFile.reset();
+
+ CleanCacheStream();
+}
+
+void OWriteStream_Impl::CleanCacheStream()
+{
+ if ( !m_xCacheStream.is() )
+ return;
+
+ try
+ {
+ uno::Reference< io::XInputStream > xInputCache = m_xCacheStream->getInputStream();
+ if ( xInputCache.is() )
+ xInputCache->closeInput();
+ }
+ catch( const uno::Exception& )
+ {}
+
+ try
+ {
+ uno::Reference< io::XOutputStream > xOutputCache = m_xCacheStream->getOutputStream();
+ if ( xOutputCache.is() )
+ xOutputCache->closeOutput();
+ }
+ catch( const uno::Exception& )
+ {}
+
+ m_xCacheStream.clear();
+ m_xCacheSeek.clear();
+}
+
+void OWriteStream_Impl::InsertIntoPackageFolder( const OUString& aName,
+ const uno::Reference< container::XNameContainer >& xParentPackageFolder )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ SAL_WARN_IF( !m_bFlushed, "package.xstor", "This method must not be called for nonflushed streams!" );
+ if ( m_bFlushed )
+ {
+ SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor", "An inserted stream is incomplete!" );
+ uno::Reference< uno::XInterface > xTmp( m_xPackageStream, uno::UNO_QUERY_THROW );
+ xParentPackageFolder->insertByName( aName, uno::Any( xTmp ) );
+
+ m_bFlushed = false;
+ m_bHasInsertedStreamOptimization = false;
+ }
+}
+bool OWriteStream_Impl::IsEncrypted()
+{
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ return false;
+
+ if ( m_bForceEncrypted || m_bHasCachedEncryptionData )
+ return true;
+
+ if ( m_oTempFile.has_value() || m_xCacheStream.is() )
+ return false;
+
+ GetStreamProperties();
+
+ // the following value can not be cached since it can change after root commit
+ bool bWasEncr = false;
+ uno::Reference< beans::XPropertySet > xPropSet( m_xPackageStream, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ uno::Any aValue = xPropSet->getPropertyValue("WasEncrypted");
+ if ( !( aValue >>= bWasEncr ) )
+ {
+ SAL_WARN( "package.xstor", "The property WasEncrypted has wrong type!" );
+ }
+ }
+
+ bool bToBeEncr = false;
+ for ( const auto& rProp : std::as_const(m_aProps) )
+ {
+ if ( rProp.Name == "Encrypted" )
+ {
+ if ( !( rProp.Value >>= bToBeEncr ) )
+ {
+ SAL_WARN( "package.xstor", "The property has wrong type!" );
+ }
+ }
+ }
+
+ // since a new key set to the package stream it should not be removed except the case when
+ // the stream becomes nonencrypted
+ uno::Sequence< beans::NamedValue > aKey;
+ if ( bToBeEncr )
+ GetEncryptionKeyProperty_Impl( xPropSet ) >>= aKey;
+
+ // If the properties must be investigated the stream is either
+ // was never changed or was changed, the parent was committed
+ // and the stream was closed.
+ // That means that if it is intended to use common storage key
+ // it is already has no encryption but is marked to be stored
+ // encrypted and the key is empty.
+ if ( !bWasEncr && bToBeEncr && !aKey.hasElements() )
+ {
+ // the stream is intended to use common storage password
+ m_bUseCommonEncryption = true;
+ return false;
+ }
+ else
+ return bToBeEncr;
+}
+
+void OWriteStream_Impl::SetDecrypted()
+{
+ SAL_WARN_IF( m_nStorageType != embed::StorageFormats::PACKAGE, "package.xstor", "The encryption is supported only for package storages!" );
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException();
+
+ GetStreamProperties();
+
+ // let the stream be modified
+ FillTempGetFileName();
+ m_bHasDataToFlush = true;
+
+ // remove encryption
+ m_bForceEncrypted = false;
+ m_bHasCachedEncryptionData = false;
+ m_aEncryptionData.clear();
+
+ for ( auto& rProp : asNonConstRange(m_aProps) )
+ {
+ if ( rProp.Name == "Encrypted" )
+ rProp.Value <<= false;
+ }
+}
+
+void OWriteStream_Impl::SetEncrypted( const ::comphelper::SequenceAsHashMap& aEncryptionData )
+{
+ SAL_WARN_IF( m_nStorageType != embed::StorageFormats::PACKAGE, "package.xstor", "The encryption is supported only for package storages!" );
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException();
+
+ if ( aEncryptionData.empty() )
+ throw uno::RuntimeException();
+
+ GetStreamProperties();
+
+ // let the stream be modified
+ FillTempGetFileName();
+ m_bHasDataToFlush = true;
+
+ // introduce encryption info
+ for ( auto& rProp : asNonConstRange(m_aProps) )
+ {
+ if ( rProp.Name == "Encrypted" )
+ rProp.Value <<= true;
+ }
+
+ m_bUseCommonEncryption = false; // very important to set it to false
+
+ m_bHasCachedEncryptionData = true;
+ m_aEncryptionData = aEncryptionData;
+}
+
+void OWriteStream_Impl::DisposeWrappers()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_pAntiImpl )
+ {
+ try {
+ m_pAntiImpl->dispose();
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+ }
+
+ m_pAntiImpl = nullptr;
+ }
+ m_pParent = nullptr;
+
+ if ( m_aInputStreamsVector.empty() )
+ return;
+
+ for ( auto& pStream : m_aInputStreamsVector )
+ {
+ if ( pStream )
+ {
+ pStream->InternalDispose();
+ pStream = nullptr;
+ }
+ }
+
+ m_aInputStreamsVector.clear();
+}
+
+void OWriteStream_Impl::GetFilledTempFileIfNo( const uno::Reference< io::XInputStream >& xStream )
+{
+ if ( !m_oTempFile.has_value() )
+ {
+ m_oTempFile.emplace();
+
+ try {
+ if ( xStream.is() )
+ {
+ // the current position of the original stream should be still OK, copy further
+ package::CopyInputToOutput( xStream, *m_oTempFile->GetStream(StreamMode::READWRITE) );
+ }
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ m_oTempFile.reset();
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ m_oTempFile.reset();
+ throw;
+ }
+
+ if ( m_oTempFile.has_value() )
+ CleanCacheStream();
+ }
+}
+
+void OWriteStream_Impl::FillTempGetFileName()
+{
+ // should try to create cache first, if the amount of contents is too big, the temp file should be taken
+ if ( !m_xCacheStream.is() && !m_oTempFile.has_value() )
+ {
+ uno::Reference< io::XInputStream > xOrigStream = m_xPackageStream->getDataStream();
+ if ( !xOrigStream.is() )
+ {
+ // in case of new inserted package stream it is possible that input stream still was not set
+ uno::Reference< io::XStream > xCacheStream = CreateMemoryStream( m_xContext );
+ SAL_WARN_IF( !xCacheStream.is(), "package.xstor", "If the stream can not be created an exception must be thrown!" );
+ m_xCacheSeek.set( xCacheStream, uno::UNO_QUERY_THROW );
+ m_xCacheStream = xCacheStream;
+ }
+ else
+ {
+ sal_Int32 nRead = 0;
+ uno::Sequence< sal_Int8 > aData( MAX_STORCACHE_SIZE + 1 );
+ nRead = xOrigStream->readBytes( aData, MAX_STORCACHE_SIZE + 1 );
+ if ( aData.getLength() > nRead )
+ aData.realloc( nRead );
+
+ if ( nRead <= MAX_STORCACHE_SIZE )
+ {
+ uno::Reference< io::XStream > xCacheStream = CreateMemoryStream( m_xContext );
+ SAL_WARN_IF( !xCacheStream.is(), "package.xstor", "If the stream can not be created an exception must be thrown!" );
+
+ if ( nRead )
+ {
+ uno::Reference< io::XOutputStream > xOutStream( xCacheStream->getOutputStream(), uno::UNO_SET_THROW );
+ xOutStream->writeBytes( aData );
+ }
+ m_xCacheSeek.set( xCacheStream, uno::UNO_QUERY_THROW );
+ m_xCacheStream = xCacheStream;
+ m_xCacheSeek->seek( 0 );
+ }
+ else if ( !m_oTempFile.has_value() )
+ {
+ m_oTempFile.emplace();
+
+ try {
+ // copy stream contents to the file
+ SvStream* pStream = m_oTempFile->GetStream(StreamMode::READWRITE);
+ pStream->WriteBytes( aData.getConstArray(), aData.getLength() );
+
+ // the current position of the original stream should be still OK, copy further
+ package::CopyInputToOutput( xOrigStream, *pStream );
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ m_oTempFile.reset();
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ m_oTempFile.reset();
+ }
+ }
+ }
+ }
+}
+
+uno::Reference< io::XStream > OWriteStream_Impl::GetTempFileAsStream()
+{
+ uno::Reference< io::XStream > xTempStream;
+
+ if ( !m_xCacheStream.is() )
+ {
+ if ( !m_oTempFile.has_value() )
+ FillTempGetFileName();
+
+ if ( m_oTempFile.has_value() )
+ {
+ // the temporary file is not used if the cache is used
+ try
+ {
+ SvStream* pStream = m_oTempFile->GetStream(StreamMode::READWRITE);
+ pStream->Seek(0);
+ xTempStream = new utl::OStreamWrapper(pStream, /*bOwner*/false);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+ }
+ }
+ }
+
+ if ( m_xCacheStream.is() )
+ xTempStream = m_xCacheStream;
+
+ // the method must always return a stream
+ // in case the stream can not be open
+ // an exception should be thrown
+ if ( !xTempStream.is() )
+ throw io::IOException("no temp stream"); //TODO:
+
+ return xTempStream;
+}
+
+uno::Reference< io::XInputStream > OWriteStream_Impl::GetTempFileAsInputStream()
+{
+ uno::Reference< io::XInputStream > xInputStream;
+
+ if ( !m_xCacheStream.is() )
+ {
+ if ( !m_oTempFile.has_value() )
+ FillTempGetFileName();
+
+ if ( m_oTempFile.has_value() )
+ {
+ // the temporary file is not used if the cache is used
+ try
+ {
+ SvStream* pStream = m_oTempFile->GetStream(StreamMode::READWRITE);
+ pStream->Seek(0);
+ xInputStream = new utl::OStreamWrapper(pStream, /*bOwner*/false);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+ }
+ }
+ }
+
+ if ( m_xCacheStream.is() )
+ xInputStream = m_xCacheStream->getInputStream();
+
+ // the method must always return a stream
+ // in case the stream can not be open
+ // an exception should be thrown
+ if ( !xInputStream.is() )
+ throw io::IOException(); // TODO:
+
+ return xInputStream;
+}
+
+void OWriteStream_Impl::InsertStreamDirectly( const uno::Reference< io::XInputStream >& xInStream,
+ const uno::Sequence< beans::PropertyValue >& aProps )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ // this call can be made only during parent storage commit
+ // the parent storage is responsible for the correct handling
+ // of deleted and renamed contents
+
+ SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor", "No package stream is set!" );
+
+ if ( m_bHasDataToFlush )
+ throw io::IOException("m_bHasDataToFlush==true");
+
+ OSL_ENSURE( !m_oTempFile.has_value() && !m_xCacheStream.is(), "The temporary must not exist!" );
+
+ // use new file as current persistent representation
+ // the new file will be removed after it's stream is closed
+ m_xPackageStream->setDataStream( xInStream );
+
+ // copy properties to the package stream
+ uno::Reference< beans::XPropertySet > xPropertySet( m_xPackageStream, uno::UNO_QUERY_THROW );
+
+ // The storage-package communication has a problem
+ // the storage caches properties, thus if the package changes one of them itself
+ // the storage does not know about it
+
+ // Depending from MediaType value the package can change the compressed property itself
+ // Thus if Compressed property is provided it must be set as the latest one
+ bool bCompressedIsSet = false;
+ bool bCompressed = false;
+ OUString aComprPropName( "Compressed" );
+ OUString aMedTypePropName( "MediaType" );
+ for ( const auto& rProp : aProps )
+ {
+ if ( rProp.Name == aComprPropName )
+ {
+ bCompressedIsSet = true;
+ rProp.Value >>= bCompressed;
+ }
+ else if ( ( m_nStorageType == embed::StorageFormats::OFOPXML || m_nStorageType == embed::StorageFormats::PACKAGE )
+ && rProp.Name == aMedTypePropName )
+ {
+ xPropertySet->setPropertyValue( rProp.Name, rProp.Value );
+ }
+ else if ( m_nStorageType == embed::StorageFormats::PACKAGE && rProp.Name == "UseCommonStoragePasswordEncryption" )
+ rProp.Value >>= m_bUseCommonEncryption;
+ else
+ throw lang::IllegalArgumentException();
+
+ // if there are cached properties update them
+ if ( rProp.Name == aMedTypePropName || rProp.Name == aComprPropName )
+ for ( auto& rMemProp : asNonConstRange(m_aProps) )
+ {
+ if ( rProp.Name == rMemProp.Name )
+ rMemProp.Value = rProp.Value;
+ }
+ }
+
+ if ( bCompressedIsSet )
+ {
+ xPropertySet->setPropertyValue( aComprPropName, uno::Any( bCompressed ) );
+ m_bCompressedSetExplicit = true;
+ }
+
+ if ( m_bUseCommonEncryption )
+ {
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException();
+
+ // set to be encrypted but do not use encryption key
+ xPropertySet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY,
+ uno::Any( uno::Sequence< beans::NamedValue >() ) );
+ xPropertySet->setPropertyValue( "Encrypted", uno::Any( true ) );
+ }
+
+ // the stream should be free soon, after package is stored
+ m_bHasDataToFlush = false;
+ m_bFlushed = true; // will allow to use transaction on stream level if will need it
+ m_bHasInsertedStreamOptimization = true;
+}
+
+void OWriteStream_Impl::Commit()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor", "No package stream is set!" );
+
+ if ( !m_bHasDataToFlush )
+ return;
+
+ uno::Reference< packages::XDataSinkEncrSupport > xNewPackageStream;
+ uno::Sequence< uno::Any > aSeq{ uno::Any(false) };
+
+ if ( m_xCacheStream.is() )
+ {
+ if ( m_pAntiImpl )
+ m_pAntiImpl->DeInit();
+
+ uno::Reference< io::XInputStream > xInStream( m_xCacheStream->getInputStream(), uno::UNO_SET_THROW );
+
+ xNewPackageStream.set( m_xPackage->createInstanceWithArguments( aSeq ), uno::UNO_QUERY_THROW );
+
+ xNewPackageStream->setDataStream( xInStream );
+
+ m_xCacheStream.clear();
+ m_xCacheSeek.clear();
+
+ }
+ else if ( m_oTempFile.has_value() )
+ {
+ if ( m_pAntiImpl )
+ m_pAntiImpl->DeInit();
+
+ uno::Reference< io::XInputStream > xInStream;
+ try
+ {
+ xInStream = new OSelfTerminateFileStream(m_xContext, std::move(*m_oTempFile));
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("package", "");
+ }
+
+ if ( !xInStream.is() )
+ throw io::IOException();
+
+ xNewPackageStream.set( m_xPackage->createInstanceWithArguments( aSeq ), uno::UNO_QUERY_THROW );
+
+ // TODO/NEW: Let the temporary file be removed after commit
+ xNewPackageStream->setDataStream( xInStream );
+ m_oTempFile.reset();
+ }
+ else // if ( m_bHasInsertedStreamOptimization )
+ {
+ // if the optimization is used the stream can be accessed directly
+ xNewPackageStream = m_xPackageStream;
+ }
+
+ // copy properties to the package stream
+ uno::Reference< beans::XPropertySet > xPropertySet( xNewPackageStream, uno::UNO_QUERY_THROW );
+
+ for ( auto& rProp : asNonConstRange(m_aProps) )
+ {
+ if ( rProp.Name == "Size" )
+ {
+ if ( m_pAntiImpl && !m_bHasInsertedStreamOptimization && m_pAntiImpl->m_xSeekable.is() )
+ {
+ rProp.Value <<= m_pAntiImpl->m_xSeekable->getLength();
+ xPropertySet->setPropertyValue( rProp.Name, rProp.Value );
+ }
+ }
+ else
+ xPropertySet->setPropertyValue( rProp.Name, rProp.Value );
+ }
+
+ if ( m_bUseCommonEncryption )
+ {
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException();
+
+ // set to be encrypted but do not use encryption key
+ xPropertySet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY,
+ uno::Any( uno::Sequence< beans::NamedValue >() ) );
+ xPropertySet->setPropertyValue( "Encrypted",
+ uno::Any( true ) );
+ }
+ else if ( m_bHasCachedEncryptionData )
+ {
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException();
+
+ xPropertySet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY,
+ uno::Any( m_aEncryptionData.getAsConstNamedValueList() ) );
+ }
+
+ // the stream should be free soon, after package is stored
+ m_xPackageStream = xNewPackageStream;
+ m_bHasDataToFlush = false;
+ m_bFlushed = true; // will allow to use transaction on stream level if will need it
+}
+
+void OWriteStream_Impl::Revert()
+{
+ // can be called only from parent storage
+ // means complete reload of the stream
+
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ if ( !m_bHasDataToFlush )
+ return; // nothing to do
+
+ OSL_ENSURE( m_oTempFile.has_value() || m_xCacheStream.is(), "The temporary must exist!" );
+
+ if ( m_xCacheStream.is() )
+ {
+ m_xCacheStream.clear();
+ m_xCacheSeek.clear();
+ }
+
+ m_oTempFile.reset();
+
+ m_aProps.realloc( 0 );
+
+ m_bHasDataToFlush = false;
+
+ m_bUseCommonEncryption = true;
+ m_bHasCachedEncryptionData = false;
+ m_aEncryptionData.clear();
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ return;
+
+ // currently the relations storage is changed only on commit
+ m_xNewRelInfoStream.clear();
+ m_aNewRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
+ if ( m_xOrigRelInfoStream.is() )
+ {
+ // the original stream is still here, that means that it was not parsed
+ m_aOrigRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
+ m_nRelInfoStatus = RELINFO_NO_INIT;
+ }
+ else
+ {
+ // the original stream was already parsed
+ if ( !m_bOrigRelInfoBroken )
+ m_nRelInfoStatus = RELINFO_READ;
+ else
+ m_nRelInfoStatus = RELINFO_BROKEN;
+ }
+}
+
+uno::Sequence< beans::PropertyValue > const & OWriteStream_Impl::GetStreamProperties()
+{
+ if ( !m_aProps.hasElements() )
+ m_aProps = ReadPackageStreamProperties();
+
+ return m_aProps;
+}
+
+uno::Sequence< beans::PropertyValue > OWriteStream_Impl::InsertOwnProps(
+ const uno::Sequence< beans::PropertyValue >& aProps,
+ bool bUseCommonEncryption )
+{
+ uno::Sequence< beans::PropertyValue > aResult( aProps );
+ beans::PropertyValue aPropVal;
+
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ aPropVal.Name = "UseCommonStoragePasswordEncryption";
+ aPropVal.Value <<= bUseCommonEncryption;
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ ReadRelInfoIfNecessary();
+
+ aPropVal.Name = "RelationsInfo";
+ if ( m_nRelInfoStatus == RELINFO_READ )
+ aPropVal.Value <<= m_aOrigRelInfo;
+ else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ || m_nRelInfoStatus == RELINFO_CHANGED )
+ aPropVal.Value <<= m_aNewRelInfo;
+ else // m_nRelInfoStatus == RELINFO_CHANGED_BROKEN || m_nRelInfoStatus == RELINFO_BROKEN
+ throw io::IOException( "Wrong relinfo stream!" );
+ }
+ if (!aPropVal.Name.isEmpty())
+ {
+ sal_Int32 i = 0;
+ for (auto p = aResult.getConstArray(); i < aResult.getLength(); ++i)
+ if (p[i].Name == aPropVal.Name)
+ break;
+ if (i == aResult.getLength())
+ aResult.realloc(i + 1);
+ aResult.getArray()[i] = aPropVal;
+ }
+
+ return aResult;
+}
+
+bool OWriteStream_Impl::IsTransacted()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+ return ( m_pAntiImpl && m_pAntiImpl->m_bTransacted );
+}
+
+void OWriteStream_Impl::ReadRelInfoIfNecessary()
+{
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ return;
+
+ if ( m_nRelInfoStatus == RELINFO_NO_INIT )
+ {
+ try
+ {
+ // Init from original stream
+ if ( m_xOrigRelInfoStream.is() )
+ m_aOrigRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(
+ m_xOrigRelInfoStream,
+ u"_rels/*.rels",
+ m_xContext );
+
+ // in case of success the stream must be thrown away, that means that the OrigRelInfo is initialized
+ // the reason for this is that the original stream might not be seekable ( at the same time the new
+ // provided stream must be seekable ), so it must be read only once
+ m_xOrigRelInfoStream.clear();
+ m_nRelInfoStatus = RELINFO_READ;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+
+ m_nRelInfoStatus = RELINFO_BROKEN;
+ m_bOrigRelInfoBroken = true;
+ }
+ }
+ else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
+ {
+ // Init from the new stream
+ try
+ {
+ if ( m_xNewRelInfoStream.is() )
+ m_aNewRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(
+ m_xNewRelInfoStream,
+ u"_rels/*.rels",
+ m_xContext );
+
+ m_nRelInfoStatus = RELINFO_CHANGED_STREAM_READ;
+ }
+ catch( const uno::Exception& )
+ {
+ m_nRelInfoStatus = RELINFO_CHANGED_BROKEN;
+ }
+ }
+}
+
+uno::Sequence< beans::PropertyValue > OWriteStream_Impl::ReadPackageStreamProperties()
+{
+ sal_Int32 nPropNum = 0;
+ if ( m_nStorageType == embed::StorageFormats::ZIP )
+ nPropNum = 2;
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ nPropNum = 3;
+ else if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ nPropNum = 4;
+ assert(nPropNum >= 2);
+ uno::Sequence< beans::PropertyValue > aResult( nPropNum );
+ auto aResultRange = asNonConstRange(aResult);
+
+ // The "Compressed" property must be set after "MediaType" property,
+ // since the setting of the last one can change the value of the first one
+ static constexpr OUStringLiteral sMediaType = u"MediaType";
+ static constexpr OUString sCompressed = u"Compressed"_ustr;
+ static constexpr OUString sSize = u"Size"_ustr;
+ static constexpr OUStringLiteral sEncrypted = u"Encrypted";
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML || m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ aResultRange[0].Name = sMediaType;
+ aResultRange[1].Name = sCompressed;
+ aResultRange[2].Name = sSize;
+
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ aResultRange[3].Name = sEncrypted;
+ }
+ else
+ {
+ aResultRange[0].Name = sCompressed;
+ aResultRange[1].Name = sSize;
+ }
+
+ // TODO: may be also raw stream should be marked
+
+ uno::Reference< beans::XPropertySet > xPropSet( m_xPackageStream, uno::UNO_QUERY_THROW );
+ for ( auto& rProp : aResultRange )
+ {
+ try {
+ rProp.Value = xPropSet->getPropertyValue( rProp.Name );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "package.xstor", "A property can't be retrieved" );
+ }
+ }
+
+ return aResult;
+}
+
+void OWriteStream_Impl::CopyInternallyTo_Impl( const uno::Reference< io::XStream >& xDestStream,
+ const ::comphelper::SequenceAsHashMap& aEncryptionData )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ SAL_WARN_IF( m_bUseCommonEncryption, "package.xstor", "The stream can not be encrypted!" );
+
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw packages::NoEncryptionException();
+
+ if ( m_pAntiImpl )
+ {
+ m_pAntiImpl->CopyToStreamInternally_Impl( xDestStream );
+ }
+ else
+ {
+ uno::Reference< io::XStream > xOwnStream = GetStream( embed::ElementModes::READ, aEncryptionData, false );
+ if ( !xOwnStream.is() )
+ throw io::IOException(); // TODO
+
+ OStorage_Impl::completeStorageStreamCopy_Impl( xOwnStream, xDestStream, m_nStorageType, GetAllRelationshipsIfAny() );
+ }
+
+ uno::Reference< embed::XEncryptionProtectedSource2 > xEncr( xDestStream, uno::UNO_QUERY );
+ if ( xEncr.is() )
+ xEncr->setEncryptionData( aEncryptionData.getAsConstNamedValueList() );
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > OWriteStream_Impl::GetAllRelationshipsIfAny()
+{
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ return uno::Sequence< uno::Sequence< beans::StringPair > >();
+
+ ReadRelInfoIfNecessary();
+
+ if ( m_nRelInfoStatus == RELINFO_READ )
+ return m_aOrigRelInfo;
+ else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ || m_nRelInfoStatus == RELINFO_CHANGED )
+ return m_aNewRelInfo;
+ else // m_nRelInfoStatus == RELINFO_CHANGED_BROKEN || m_nRelInfoStatus == RELINFO_BROKEN
+ throw io::IOException( "Wrong relinfo stream!" );
+}
+
+void OWriteStream_Impl::CopyInternallyTo_Impl( const uno::Reference< io::XStream >& xDestStream )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ if ( m_pAntiImpl )
+ {
+ m_pAntiImpl->CopyToStreamInternally_Impl( xDestStream );
+ }
+ else
+ {
+ uno::Reference< io::XStream > xOwnStream = GetStream( embed::ElementModes::READ, false );
+ if ( !xOwnStream.is() )
+ throw io::IOException(); // TODO
+
+ OStorage_Impl::completeStorageStreamCopy_Impl( xOwnStream, xDestStream, m_nStorageType, GetAllRelationshipsIfAny() );
+ }
+}
+
+uno::Reference< io::XStream > OWriteStream_Impl::GetStream( sal_Int32 nStreamMode, const ::comphelper::SequenceAsHashMap& aEncryptionData, bool bHierarchyAccess )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor", "No package stream is set!" );
+
+ if ( m_pAntiImpl )
+ throw io::IOException(); // TODO:
+
+ if ( !IsEncrypted() )
+ throw packages::NoEncryptionException();
+
+ uno::Reference< io::XStream > xResultStream;
+
+ uno::Reference< beans::XPropertySet > xPropertySet( m_xPackageStream, uno::UNO_QUERY_THROW );
+
+ if ( m_bHasCachedEncryptionData )
+ {
+ if ( !::package::PackageEncryptionDataLessOrEqual( m_aEncryptionData, aEncryptionData ) )
+ throw packages::WrongPasswordException();
+
+ // the correct key must be set already
+ xResultStream = GetStream_Impl( nStreamMode, bHierarchyAccess );
+ }
+ else
+ {
+ SetEncryptionKeyProperty_Impl( xPropertySet, aEncryptionData.getAsConstNamedValueList() );
+
+ try {
+ xResultStream = GetStream_Impl( nStreamMode, bHierarchyAccess );
+
+ m_bUseCommonEncryption = false; // very important to set it to false
+ m_bHasCachedEncryptionData = true;
+ m_aEncryptionData = aEncryptionData;
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
+ throw;
+ }
+ catch ( const uno::Exception& ex )
+ {
+ TOOLS_WARN_EXCEPTION("package.xstor", "GetStream: decrypting stream failed");
+ SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
+ throw io::IOException(ex.Message); // TODO:
+ }
+ }
+
+ SAL_WARN_IF( !xResultStream.is(), "package.xstor", "In case stream can not be retrieved an exception must be thrown!" );
+
+ return xResultStream;
+}
+
+uno::Reference< io::XStream > OWriteStream_Impl::GetStream( sal_Int32 nStreamMode, bool bHierarchyAccess )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor", "No package stream is set!" );
+
+ if ( m_pAntiImpl )
+ throw io::IOException(); // TODO:
+
+ uno::Reference< io::XStream > xResultStream;
+
+ if ( IsEncrypted() )
+ {
+ ::comphelper::SequenceAsHashMap aGlobalEncryptionData;
+ try
+ {
+ aGlobalEncryptionData = GetCommonRootEncryptionData();
+ }
+ catch( const packages::NoEncryptionException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw packages::WrongPasswordException();
+ }
+
+ xResultStream = GetStream( nStreamMode, aGlobalEncryptionData, bHierarchyAccess );
+ }
+ else
+ xResultStream = GetStream_Impl( nStreamMode, bHierarchyAccess );
+
+ return xResultStream;
+}
+
+uno::Reference< io::XStream > OWriteStream_Impl::GetStream_Impl( sal_Int32 nStreamMode, bool bHierarchyAccess )
+{
+ // private method, no mutex is used
+ GetStreamProperties();
+
+ // TODO/LATER: this info might be read later, on demand in future
+ ReadRelInfoIfNecessary();
+
+ if ( ( nStreamMode & embed::ElementModes::READWRITE ) == embed::ElementModes::READ )
+ {
+ uno::Reference< io::XInputStream > xInStream;
+ if ( m_xCacheStream.is() || m_oTempFile.has_value() )
+ xInStream = GetTempFileAsInputStream(); //TODO:
+ else
+ xInStream = m_xPackageStream->getDataStream();
+
+ // The stream does not exist in the storage
+ if ( !xInStream.is() )
+ throw io::IOException();
+
+ rtl::Reference<OInputCompStream> pStream = new OInputCompStream( *this, xInStream, InsertOwnProps( m_aProps, m_bUseCommonEncryption ), m_nStorageType );
+ m_aInputStreamsVector.push_back( pStream.get() );
+ return pStream;
+ }
+ else if ( ( nStreamMode & embed::ElementModes::READWRITE ) == embed::ElementModes::SEEKABLEREAD )
+ {
+ if ( !m_xCacheStream.is() && !m_oTempFile.has_value() && !( m_xPackageStream->getDataStream().is() ) )
+ {
+ // The stream does not exist in the storage
+ throw io::IOException();
+ }
+
+ uno::Reference< io::XInputStream > xInStream = GetTempFileAsInputStream(); //TODO:
+
+ if ( !xInStream.is() )
+ throw io::IOException();
+
+ rtl::Reference<OInputSeekStream> pStream = new OInputSeekStream( *this, xInStream, InsertOwnProps( m_aProps, m_bUseCommonEncryption ), m_nStorageType );
+ m_aInputStreamsVector.push_back( pStream.get() );
+ return pStream;
+ }
+ else if ( ( nStreamMode & embed::ElementModes::WRITE ) == embed::ElementModes::WRITE )
+ {
+ if ( !m_aInputStreamsVector.empty() )
+ throw io::IOException(); // TODO:
+
+ uno::Reference< io::XStream > xStream;
+ if ( ( nStreamMode & embed::ElementModes::TRUNCATE ) == embed::ElementModes::TRUNCATE )
+ {
+ m_oTempFile.reset();
+ if ( m_xCacheStream.is() )
+ CleanCacheStream();
+
+ m_bHasDataToFlush = true;
+
+ // this call is triggered by the parent and it will recognize the change of the state
+ if ( m_pParent )
+ m_pParent->m_bIsModified = true;
+
+ xStream = CreateMemoryStream( m_xContext );
+ m_xCacheSeek.set( xStream, uno::UNO_QUERY_THROW );
+ m_xCacheStream = xStream;
+ }
+ else if ( !m_bHasInsertedStreamOptimization )
+ {
+ if ( !m_oTempFile.has_value() && !m_xCacheStream.is() && !( m_xPackageStream->getDataStream().is() ) )
+ {
+ // The stream does not exist in the storage
+ m_bHasDataToFlush = true;
+
+ // this call is triggered by the parent and it will recognize the change of the state
+ if ( m_pParent )
+ m_pParent->m_bIsModified = true;
+ xStream = GetTempFileAsStream();
+ }
+
+ // if the stream exists the temporary file is created on demand
+ // xStream = GetTempFileAsStream();
+ }
+
+ rtl::Reference<OWriteStream> tmp;
+ assert(m_xMutex.is() && "No mutex!");
+ if ( !xStream.is() )
+ tmp = new OWriteStream( *this, bHierarchyAccess );
+ else
+ tmp = new OWriteStream( *this, xStream, bHierarchyAccess );
+
+ m_pAntiImpl = tmp.get();
+ return tmp;
+ }
+
+ throw lang::IllegalArgumentException(); // TODO
+}
+
+uno::Reference< io::XInputStream > OWriteStream_Impl::GetPlainRawInStream()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor", "No package stream is set!" );
+
+ // this method is used only internally, this stream object should not go outside of this implementation
+ // if ( m_pAntiImpl )
+ // throw io::IOException(); // TODO:
+
+ return m_xPackageStream->getPlainRawStream();
+}
+
+uno::Reference< io::XInputStream > OWriteStream_Impl::GetRawInStream()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor", "No package stream is set!" );
+
+ if ( m_pAntiImpl )
+ throw io::IOException(); // TODO:
+
+ SAL_WARN_IF( !IsEncrypted(), "package.xstor", "Impossible to get raw representation for nonencrypted stream!" );
+ if ( !IsEncrypted() )
+ throw packages::NoEncryptionException();
+
+ return m_xPackageStream->getRawStream();
+}
+
+::comphelper::SequenceAsHashMap OWriteStream_Impl::GetCommonRootEncryptionData()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE || !m_pParent )
+ throw packages::NoEncryptionException();
+
+ return m_pParent->GetCommonRootEncryptionData();
+}
+
+void OWriteStream_Impl::InputStreamDisposed( OInputCompStream* pStream )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ std::erase(m_aInputStreamsVector, pStream);
+}
+
+void OWriteStream_Impl::CreateReadonlyCopyBasedOnData( const uno::Reference< io::XInputStream >& xDataToCopy, const uno::Sequence< beans::PropertyValue >& aProps, uno::Reference< io::XStream >& xTargetStream )
+{
+ uno::Reference < io::XStream > xTempFile;
+ if ( !xTargetStream.is() )
+ xTempFile = new utl::TempFileFastService;
+ else
+ xTempFile = xTargetStream;
+
+ uno::Reference < io::XSeekable > xTempSeek( xTempFile, uno::UNO_QUERY_THROW );
+
+ uno::Reference < io::XOutputStream > xTempOut(xTempFile->getOutputStream(), uno::UNO_SET_THROW);
+
+ if ( xDataToCopy.is() )
+ ::comphelper::OStorageHelper::CopyInputToOutput( xDataToCopy, xTempOut );
+
+ xTempOut->closeOutput();
+ xTempSeek->seek( 0 );
+
+ uno::Reference< io::XInputStream > xInStream = xTempFile->getInputStream();
+ if ( !xInStream.is() )
+ throw io::IOException();
+
+ // TODO: remember last state of m_bUseCommonEncryption
+ if ( !xTargetStream.is() )
+ xTargetStream.set(
+ new OInputSeekStream( xInStream, InsertOwnProps( aProps, m_bUseCommonEncryption ), m_nStorageType ) );
+}
+
+void OWriteStream_Impl::GetCopyOfLastCommit( uno::Reference< io::XStream >& xTargetStream )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor", "The source stream for copying is incomplete!" );
+ if ( !m_xPackageStream.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< io::XInputStream > xDataToCopy;
+ if ( IsEncrypted() )
+ {
+ // an encrypted stream must contain input stream
+ ::comphelper::SequenceAsHashMap aGlobalEncryptionData;
+ try
+ {
+ aGlobalEncryptionData = GetCommonRootEncryptionData();
+ }
+ catch( const packages::NoEncryptionException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "No Element");
+ throw packages::WrongPasswordException();
+ }
+
+ GetCopyOfLastCommit( xTargetStream, aGlobalEncryptionData );
+ }
+ else
+ {
+ xDataToCopy = m_xPackageStream->getDataStream();
+
+ // in case of new inserted package stream it is possible that input stream still was not set
+ GetStreamProperties();
+
+ CreateReadonlyCopyBasedOnData( xDataToCopy, m_aProps, xTargetStream );
+ }
+}
+
+void OWriteStream_Impl::GetCopyOfLastCommit( uno::Reference< io::XStream >& xTargetStream, const ::comphelper::SequenceAsHashMap& aEncryptionData )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor", "The source stream for copying is incomplete!" );
+ if ( !m_xPackageStream.is() )
+ throw uno::RuntimeException();
+
+ if ( !IsEncrypted() )
+ throw packages::NoEncryptionException();
+
+ uno::Reference< io::XInputStream > xDataToCopy;
+
+ if ( m_bHasCachedEncryptionData )
+ {
+ // TODO: introduce last committed cashed password information and use it here
+ // that means "use common pass" also should be remembered on flash
+ uno::Sequence< beans::NamedValue > aKey = aEncryptionData.getAsConstNamedValueList();
+
+ uno::Reference< beans::XPropertySet > xProps( m_xPackageStream, uno::UNO_QUERY_THROW );
+
+ bool bEncr = false;
+ xProps->getPropertyValue( "Encrypted" ) >>= bEncr;
+ if ( !bEncr )
+ throw packages::NoEncryptionException();
+
+ uno::Sequence< beans::NamedValue > aPackKey;
+ xProps->getPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY ) >>= aPackKey;
+ if ( !SequencesEqual( aKey, aPackKey ) )
+ throw packages::WrongPasswordException();
+
+ // the correct key must be set already
+ xDataToCopy = m_xPackageStream->getDataStream();
+ }
+ else
+ {
+ uno::Reference< beans::XPropertySet > xPropertySet( m_xPackageStream, uno::UNO_QUERY );
+ SetEncryptionKeyProperty_Impl( xPropertySet, aEncryptionData.getAsConstNamedValueList() );
+
+ try {
+ xDataToCopy = m_xPackageStream->getDataStream();
+
+ if ( !xDataToCopy.is() )
+ {
+ SAL_WARN( "package.xstor", "Encrypted ZipStream must already have input stream inside!" );
+ SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "package.xstor", "Can't open encrypted stream");
+ SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
+ throw;
+ }
+
+ SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
+ }
+
+ // in case of new inserted package stream it is possible that input stream still was not set
+ GetStreamProperties();
+
+ CreateReadonlyCopyBasedOnData( xDataToCopy, m_aProps, xTargetStream );
+}
+
+void OWriteStream_Impl::CommitStreamRelInfo( const uno::Reference< embed::XStorage >& xRelStorage, std::u16string_view aOrigStreamName, std::u16string_view aNewStreamName )
+{
+ // at this point of time the old stream must be already cleaned
+ OSL_ENSURE( m_nStorageType == embed::StorageFormats::OFOPXML, "The method should be used only with OFOPXML format!" );
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ return;
+
+ OSL_ENSURE( !aOrigStreamName.empty() && !aNewStreamName.empty() && xRelStorage.is(),
+ "Wrong relation persistence information is provided!" );
+
+ if ( !xRelStorage.is() || aOrigStreamName.empty() || aNewStreamName.empty() )
+ throw uno::RuntimeException();
+
+ if ( m_nRelInfoStatus == RELINFO_BROKEN || m_nRelInfoStatus == RELINFO_CHANGED_BROKEN )
+ throw io::IOException(); // TODO:
+
+ OUString aOrigRelStreamName = OUString::Concat(aOrigStreamName) + ".rels";
+ OUString aNewRelStreamName = OUString::Concat(aNewStreamName) + ".rels";
+
+ bool bRenamed = aOrigRelStreamName != aNewRelStreamName;
+ if ( m_nRelInfoStatus == RELINFO_CHANGED
+ || m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ
+ || m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
+ {
+ if ( bRenamed && xRelStorage->hasByName( aOrigRelStreamName ) )
+ xRelStorage->removeElement( aOrigRelStreamName );
+
+ if ( m_nRelInfoStatus == RELINFO_CHANGED )
+ {
+ if ( m_aNewRelInfo.hasElements() )
+ {
+ uno::Reference< io::XStream > xRelsStream =
+ xRelStorage->openStreamElement( aNewRelStreamName,
+ embed::ElementModes::TRUNCATE | embed::ElementModes::READWRITE );
+
+ uno::Reference< io::XOutputStream > xOutStream = xRelsStream->getOutputStream();
+ if ( !xOutStream.is() )
+ throw uno::RuntimeException();
+
+ ::comphelper::OFOPXMLHelper::WriteRelationsInfoSequence( xOutStream, m_aNewRelInfo, m_xContext );
+
+ // set the mediatype
+ uno::Reference< beans::XPropertySet > xPropSet( xRelsStream, uno::UNO_QUERY_THROW );
+ xPropSet->setPropertyValue(
+ "MediaType",
+ uno::Any( OUString("application/vnd.openxmlformats-package.relationships+xml" ) ) );
+
+ m_nRelInfoStatus = RELINFO_READ;
+ }
+ }
+ else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ
+ || m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
+ {
+ uno::Reference< io::XStream > xRelsStream =
+ xRelStorage->openStreamElement( aNewRelStreamName,
+ embed::ElementModes::TRUNCATE | embed::ElementModes::READWRITE );
+
+ uno::Reference< io::XOutputStream > xOutputStream = xRelsStream->getOutputStream();
+ if ( !xOutputStream.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< io::XSeekable > xSeek( m_xNewRelInfoStream, uno::UNO_QUERY_THROW );
+ xSeek->seek( 0 );
+ ::comphelper::OStorageHelper::CopyInputToOutput( m_xNewRelInfoStream, xOutputStream );
+ xSeek->seek( 0 );
+
+ // set the mediatype
+ uno::Reference< beans::XPropertySet > xPropSet( xRelsStream, uno::UNO_QUERY_THROW );
+ xPropSet->setPropertyValue("MediaType",
+ uno::Any( OUString("application/vnd.openxmlformats-package.relationships+xml" ) ) );
+
+ if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
+ m_nRelInfoStatus = RELINFO_NO_INIT;
+ else
+ {
+ // the information is already parsed and the stream is stored, no need in temporary stream any more
+ m_xNewRelInfoStream.clear();
+ m_nRelInfoStatus = RELINFO_READ;
+ }
+ }
+
+ // the original stream makes no sense after this step
+ m_xOrigRelInfoStream = m_xNewRelInfoStream;
+ m_aOrigRelInfo = m_aNewRelInfo;
+ m_bOrigRelInfoBroken = false;
+ m_aNewRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
+ m_xNewRelInfoStream.clear();
+ }
+ else
+ {
+ // the stream is not changed but it might be renamed
+ if ( bRenamed && xRelStorage->hasByName( aOrigRelStreamName ) )
+ xRelStorage->renameElement( aOrigRelStreamName, aNewRelStreamName );
+ }
+}
+
+// OWriteStream implementation
+
+OWriteStream::OWriteStream( OWriteStream_Impl& rImpl, bool bTransacted )
+: m_pImpl( &rImpl )
+, m_xSharedMutex( rImpl.m_xMutex )
+, m_aListenersContainer( rImpl.m_xMutex->GetMutex() )
+, m_nStorageType( m_pImpl->m_nStorageType )
+, m_bInStreamDisconnected( false )
+, m_bInitOnDemand( true )
+, m_nInitPosition( 0 )
+, m_bTransacted( bTransacted )
+{
+}
+
+OWriteStream::OWriteStream( OWriteStream_Impl& rImpl, uno::Reference< io::XStream > const & xStream, bool bTransacted )
+: m_pImpl( &rImpl )
+, m_xSharedMutex( rImpl.m_xMutex )
+, m_aListenersContainer( rImpl.m_xMutex->GetMutex() )
+, m_nStorageType( m_pImpl->m_nStorageType )
+, m_bInStreamDisconnected( false )
+, m_bInitOnDemand( false )
+, m_nInitPosition( 0 )
+, m_bTransacted( bTransacted )
+{
+ if ( xStream.is() )
+ {
+ m_xInStream = xStream->getInputStream();
+ m_xOutStream = xStream->getOutputStream();
+ m_xSeekable.set( xStream, uno::UNO_QUERY );
+ OSL_ENSURE( m_xInStream.is() && m_xOutStream.is() && m_xSeekable.is(), "Stream implementation is incomplete!" );
+ }
+}
+
+OWriteStream::~OWriteStream()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+ if ( m_pImpl )
+ {
+ osl_atomic_increment(&m_refCount);
+ try {
+ dispose();
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+ }
+ }
+}
+
+void OWriteStream::DeInit()
+{
+ if ( !m_pImpl )
+ return; // do nothing
+
+ if ( m_xSeekable.is() )
+ m_nInitPosition = m_xSeekable->getPosition();
+
+ m_xInStream.clear();
+ m_xOutStream.clear();
+ m_xSeekable.clear();
+ m_bInitOnDemand = true;
+}
+
+void OWriteStream::CheckInitOnDemand()
+{
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bInitOnDemand )
+ return;
+
+ SAL_INFO( "package.xstor", "package (mv76033) OWriteStream::CheckInitOnDemand, initializing" );
+ uno::Reference< io::XStream > xStream = m_pImpl->GetTempFileAsStream();
+ if ( xStream.is() )
+ {
+ m_xInStream.set( xStream->getInputStream(), uno::UNO_SET_THROW );
+ m_xOutStream.set( xStream->getOutputStream(), uno::UNO_SET_THROW );
+ m_xSeekable.set( xStream, uno::UNO_QUERY_THROW );
+ m_xSeekable->seek( m_nInitPosition );
+
+ m_nInitPosition = 0;
+ m_bInitOnDemand = false;
+ }
+}
+
+void OWriteStream::CopyToStreamInternally_Impl( const uno::Reference< io::XStream >& xDest )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ CheckInitOnDemand();
+
+ if ( !m_xInStream.is() )
+ throw uno::RuntimeException();
+
+ if ( !m_xSeekable.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< beans::XPropertySet > xDestProps( xDest, uno::UNO_QUERY_THROW );
+
+ uno::Reference< io::XOutputStream > xDestOutStream = xDest->getOutputStream();
+ if ( !xDestOutStream.is() )
+ throw io::IOException(); // TODO
+
+ sal_Int64 nCurPos = m_xSeekable->getPosition();
+ m_xSeekable->seek( 0 );
+
+ uno::Exception eThrown;
+ bool bThrown = false;
+ try {
+ ::comphelper::OStorageHelper::CopyInputToOutput( m_xInStream, xDestOutStream );
+ }
+ catch ( const uno::Exception& e )
+ {
+ eThrown = e;
+ bThrown = true;
+ }
+
+ // position-related section below is critical
+ // if it fails the stream will become invalid
+ try {
+ m_xSeekable->seek( nCurPos );
+ }
+ catch ( const uno::Exception& )
+ {
+ // TODO: set the stream in invalid state or dispose
+ TOOLS_WARN_EXCEPTION( "package.xstor", "The stream become invalid during copying" );
+ throw uno::RuntimeException();
+ }
+
+ if ( bThrown )
+ throw eThrown;
+
+ // now the properties can be copied
+ // the order of the properties setting is not important for StorageStream API
+ OUString aPropName ("Compressed");
+ xDestProps->setPropertyValue( aPropName, getPropertyValue( aPropName ) );
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE || m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ aPropName = "MediaType";
+ xDestProps->setPropertyValue( aPropName, getPropertyValue( aPropName ) );
+
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ aPropName = "UseCommonStoragePasswordEncryption";
+ xDestProps->setPropertyValue( aPropName, getPropertyValue( aPropName ) );
+ }
+ }
+}
+
+void OWriteStream::ModifyParentUnlockMutex_Impl(osl::ClearableMutexGuard& aGuard)
+{
+ if ( m_pImpl->m_pParent )
+ {
+ if ( m_pImpl->m_pParent->HasModifiedListener() )
+ {
+ uno::Reference< util::XModifiable > xParentModif( static_cast<util::XModifiable*>(m_pImpl->m_pParent->m_pAntiImpl) );
+ aGuard.clear();
+ xParentModif->setModified( true );
+ }
+ else
+ m_pImpl->m_pParent->m_bIsModified = true;
+ }
+}
+
+uno::Any SAL_CALL OWriteStream::queryInterface( const uno::Type& rType )
+{
+ // common interfaces
+ uno::Any aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<lang::XTypeProvider*> ( this )
+ , static_cast<io::XInputStream*> ( this )
+ , static_cast<io::XOutputStream*> ( this )
+ , static_cast<io::XStream*> ( this )
+ , static_cast<embed::XExtendedStorageStream*> ( this )
+ , static_cast<io::XSeekable*> ( this )
+ , static_cast<io::XTruncate*> ( this )
+ , static_cast<lang::XComponent*> ( this )
+ , static_cast<beans::XPropertySet*> ( this ) );
+
+ if ( aReturn.hasValue() )
+ return aReturn ;
+
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<embed::XEncryptionProtectedSource2*> ( this )
+ , static_cast<embed::XEncryptionProtectedSource*> ( this ) );
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<embed::XRelationshipAccess*> ( this ) );
+ }
+
+ if ( aReturn.hasValue() )
+ return aReturn ;
+
+ if ( m_bTransacted )
+ {
+ aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<embed::XTransactedObject*> ( this )
+ , static_cast<embed::XTransactionBroadcaster*> ( this ) );
+
+ if ( aReturn.hasValue() )
+ return aReturn ;
+ }
+
+ return OWeakObject::queryInterface( rType );
+}
+
+void SAL_CALL OWriteStream::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL OWriteStream::release() noexcept
+{
+ OWeakObject::release();
+}
+
+uno::Sequence< uno::Type > SAL_CALL OWriteStream::getTypes()
+{
+ if (! m_oTypeCollection)
+ {
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if (! m_oTypeCollection)
+ {
+ if ( m_bTransacted )
+ {
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ ::cppu::OTypeCollection aTmpCollection
+ ( cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<io::XInputStream>::get()
+ , cppu::UnoType<io::XOutputStream>::get()
+ , cppu::UnoType<io::XStream>::get()
+ , cppu::UnoType<io::XSeekable>::get()
+ , cppu::UnoType<io::XTruncate>::get()
+ , cppu::UnoType<lang::XComponent>::get()
+ , cppu::UnoType<embed::XEncryptionProtectedSource2>::get()
+ , cppu::UnoType<embed::XEncryptionProtectedSource>::get()
+ , cppu::UnoType<embed::XExtendedStorageStream>::get()
+ , cppu::UnoType<embed::XTransactedObject>::get()
+ , cppu::UnoType<embed::XTransactionBroadcaster>::get());
+
+ m_oTypeCollection.emplace(
+ cppu::UnoType<beans::XPropertySet>::get()
+ , aTmpCollection.getTypes());
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ m_oTypeCollection.emplace(
+ cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<io::XInputStream>::get()
+ , cppu::UnoType<io::XOutputStream>::get()
+ , cppu::UnoType<io::XStream>::get()
+ , cppu::UnoType<io::XSeekable>::get()
+ , cppu::UnoType<io::XTruncate>::get()
+ , cppu::UnoType<lang::XComponent>::get()
+ , cppu::UnoType<embed::XRelationshipAccess>::get()
+ , cppu::UnoType<embed::XExtendedStorageStream>::get()
+ , cppu::UnoType<embed::XTransactedObject>::get()
+ , cppu::UnoType<embed::XTransactionBroadcaster>::get()
+ , cppu::UnoType<beans::XPropertySet>::get());
+ }
+ else // if ( m_pData->m_nStorageType == embed::StorageFormats::ZIP )
+ {
+ m_oTypeCollection.emplace(
+ cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<io::XInputStream>::get()
+ , cppu::UnoType<io::XOutputStream>::get()
+ , cppu::UnoType<io::XStream>::get()
+ , cppu::UnoType<io::XSeekable>::get()
+ , cppu::UnoType<io::XTruncate>::get()
+ , cppu::UnoType<lang::XComponent>::get()
+ , cppu::UnoType<embed::XExtendedStorageStream>::get()
+ , cppu::UnoType<embed::XTransactedObject>::get()
+ , cppu::UnoType<embed::XTransactionBroadcaster>::get()
+ , cppu::UnoType<beans::XPropertySet>::get());
+ }
+ }
+ else
+ {
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ m_oTypeCollection.emplace(
+ cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<io::XInputStream>::get()
+ , cppu::UnoType<io::XOutputStream>::get()
+ , cppu::UnoType<io::XStream>::get()
+ , cppu::UnoType<io::XSeekable>::get()
+ , cppu::UnoType<io::XTruncate>::get()
+ , cppu::UnoType<lang::XComponent>::get()
+ , cppu::UnoType<embed::XEncryptionProtectedSource2>::get()
+ , cppu::UnoType<embed::XEncryptionProtectedSource>::get()
+ , cppu::UnoType<beans::XPropertySet>::get());
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ m_oTypeCollection.emplace(
+ cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<io::XInputStream>::get()
+ , cppu::UnoType<io::XOutputStream>::get()
+ , cppu::UnoType<io::XStream>::get()
+ , cppu::UnoType<io::XSeekable>::get()
+ , cppu::UnoType<io::XTruncate>::get()
+ , cppu::UnoType<lang::XComponent>::get()
+ , cppu::UnoType<embed::XRelationshipAccess>::get()
+ , cppu::UnoType<beans::XPropertySet>::get());
+ }
+ else // if ( m_pData->m_nStorageType == embed::StorageFormats::ZIP )
+ {
+ m_oTypeCollection.emplace(
+ cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<io::XInputStream>::get()
+ , cppu::UnoType<io::XOutputStream>::get()
+ , cppu::UnoType<io::XStream>::get()
+ , cppu::UnoType<io::XSeekable>::get()
+ , cppu::UnoType<io::XTruncate>::get()
+ , cppu::UnoType<lang::XComponent>::get()
+ , cppu::UnoType<beans::XPropertySet>::get());
+ }
+ }
+ }
+ }
+
+ return m_oTypeCollection->getTypes() ;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL OWriteStream::getImplementationId()
+{
+ static const comphelper::UnoIdInit lcl_ImplId;
+ return lcl_ImplId.getSeq();
+}
+
+sal_Int32 SAL_CALL OWriteStream::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xInStream.is() )
+ throw io::NotConnectedException();
+
+ return m_xInStream->readBytes( aData, nBytesToRead );
+}
+
+sal_Int32 SAL_CALL OWriteStream::readSomeBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xInStream.is() )
+ throw io::NotConnectedException();
+
+ return m_xInStream->readSomeBytes( aData, nMaxBytesToRead );
+}
+
+void SAL_CALL OWriteStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xInStream.is() )
+ throw io::NotConnectedException();
+
+ m_xInStream->skipBytes( nBytesToSkip );
+}
+
+sal_Int32 SAL_CALL OWriteStream::available( )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xInStream.is() )
+ throw io::NotConnectedException();
+
+ return m_xInStream->available();
+
+}
+
+void SAL_CALL OWriteStream::closeInput( )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bInitOnDemand && ( m_bInStreamDisconnected || !m_xInStream.is() ) )
+ throw io::NotConnectedException();
+
+ // the input part of the stream stays open for internal purposes (to allow reading during copying)
+ // since it can not be reopened until output part is closed, it will be closed with output part.
+ m_bInStreamDisconnected = true;
+ // m_xInStream->closeInput();
+ // m_xInStream.clear();
+
+ if ( !m_xOutStream.is() )
+ dispose();
+}
+
+uno::Reference< io::XInputStream > SAL_CALL OWriteStream::getInputStream()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bInitOnDemand && ( m_bInStreamDisconnected || !m_xInStream.is() ) )
+ return uno::Reference< io::XInputStream >();
+
+ return this;
+}
+
+uno::Reference< io::XOutputStream > SAL_CALL OWriteStream::getOutputStream()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ try
+ {
+ CheckInitOnDemand();
+ }
+ catch( const io::IOException& r )
+ {
+ throw lang::WrappedTargetRuntimeException("OWriteStream::getOutputStream: Could not create backing temp file",
+ getXWeak(), css::uno::Any ( r ) );
+ }
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xOutStream.is() )
+ return uno::Reference< io::XOutputStream >();
+
+ return this;
+}
+
+void SAL_CALL OWriteStream::writeBytes( const uno::Sequence< sal_Int8 >& aData )
+{
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ // the write method makes initialization itself, since it depends from the aData length
+ // NO CheckInitOnDemand()!
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bInitOnDemand )
+ {
+ if ( !m_xOutStream.is() || !m_xSeekable.is())
+ throw io::NotConnectedException();
+
+ if ( m_pImpl->m_xCacheStream.is() )
+ {
+ // check whether the cache should be turned off
+ sal_Int64 nPos = m_xSeekable->getPosition();
+ if ( nPos + aData.getLength() > MAX_STORCACHE_SIZE )
+ {
+ // disconnect the cache and copy the data to the temporary file
+ m_xSeekable->seek( 0 );
+
+ // it is enough to copy the cached stream, the cache should already contain everything
+ m_pImpl->GetFilledTempFileIfNo( m_xInStream );
+ if ( m_pImpl->m_oTempFile.has_value() )
+ {
+ DeInit();
+ // the last position is known and it is differs from the current stream position
+ m_nInitPosition = nPos;
+ }
+ }
+ }
+ }
+
+ if ( m_bInitOnDemand )
+ {
+ SAL_INFO( "package.xstor", "package (mv76033) OWriteStream::CheckInitOnDemand, initializing" );
+ uno::Reference< io::XStream > xStream = m_pImpl->GetTempFileAsStream();
+ if ( xStream.is() )
+ {
+ m_xInStream.set( xStream->getInputStream(), uno::UNO_SET_THROW );
+ m_xOutStream.set( xStream->getOutputStream(), uno::UNO_SET_THROW );
+ m_xSeekable.set( xStream, uno::UNO_QUERY_THROW );
+ m_xSeekable->seek( m_nInitPosition );
+
+ m_nInitPosition = 0;
+ m_bInitOnDemand = false;
+ }
+ }
+
+ if ( !m_xOutStream.is() )
+ throw io::NotConnectedException();
+
+ m_xOutStream->writeBytes( aData );
+ m_pImpl->m_bHasDataToFlush = true;
+
+ ModifyParentUnlockMutex_Impl( aGuard );
+}
+
+void OWriteStream::writeBytes( const sal_Int8* pData, sal_Int32 nBytesToWrite )
+{
+ assert(nBytesToWrite >= 0);
+
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ // the write method makes initialization itself, since it depends from the aData length
+ // NO CheckInitOnDemand()!
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bInitOnDemand )
+ {
+ if ( !m_xOutStream.is() || !m_xSeekable.is())
+ throw io::NotConnectedException();
+
+ if ( m_pImpl->m_xCacheStream.is() )
+ {
+ // check whether the cache should be turned off
+ sal_Int64 nPos = m_xSeekable->getPosition();
+ if ( nPos + nBytesToWrite > MAX_STORCACHE_SIZE )
+ {
+ // disconnect the cache and copy the data to the temporary file
+ m_xSeekable->seek( 0 );
+
+ // it is enough to copy the cached stream, the cache should already contain everything
+ m_pImpl->GetFilledTempFileIfNo( m_xInStream );
+ if ( m_pImpl->m_oTempFile.has_value() )
+ {
+ DeInit();
+ // the last position is known and it is differs from the current stream position
+ m_nInitPosition = nPos;
+ }
+ }
+ }
+ }
+
+ if ( m_bInitOnDemand )
+ {
+ SAL_INFO( "package.xstor", "package (mv76033) OWriteStream::CheckInitOnDemand, initializing" );
+ uno::Reference< io::XStream > xStream = m_pImpl->GetTempFileAsStream();
+ if ( xStream.is() )
+ {
+ m_xInStream.set( xStream->getInputStream(), uno::UNO_SET_THROW );
+ m_xOutStream.set( xStream->getOutputStream(), uno::UNO_SET_THROW );
+ m_xSeekable.set( xStream, uno::UNO_QUERY_THROW );
+ m_xSeekable->seek( m_nInitPosition );
+
+ m_nInitPosition = 0;
+ m_bInitOnDemand = false;
+ }
+ }
+
+ if ( !m_xOutStream.is() )
+ throw io::NotConnectedException();
+
+ if (auto pByteWriter = dynamic_cast< comphelper::ByteWriter* >( m_xOutStream.get() ))
+ pByteWriter->writeBytes(pData, nBytesToWrite);
+ else
+ {
+ uno::Sequence<sal_Int8> aData(pData, nBytesToWrite);
+ m_xOutStream->writeBytes( aData );
+ }
+ m_pImpl->m_bHasDataToFlush = true;
+
+ ModifyParentUnlockMutex_Impl( aGuard );
+}
+
+void SAL_CALL OWriteStream::flush()
+{
+ // In case stream is flushed its current version becomes visible
+ // to the parent storage. Usually parent storage flushes the stream
+ // during own commit but a user can explicitly flush the stream
+ // so the changes will be available through cloning functionality.
+
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bInitOnDemand )
+ {
+ if ( !m_xOutStream.is() )
+ throw io::NotConnectedException();
+
+ m_xOutStream->flush();
+ m_pImpl->Commit();
+ }
+}
+
+void OWriteStream::CloseOutput_Impl()
+{
+ // all the checks must be done in calling method
+
+ m_xOutStream->closeOutput();
+ m_xOutStream.clear();
+
+ if ( m_bInitOnDemand )
+ return;
+
+ // after the stream is disposed it can be committed
+ // so transport correct size property
+ if ( !m_xSeekable.is() )
+ throw uno::RuntimeException();
+
+ for ( auto& rProp : asNonConstRange(m_pImpl->m_aProps) )
+ {
+ if ( rProp.Name == "Size" )
+ rProp.Value <<= m_xSeekable->getLength();
+ }
+}
+
+void SAL_CALL OWriteStream::closeOutput()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xOutStream.is() )
+ throw io::NotConnectedException();
+
+ CloseOutput_Impl();
+
+ if ( m_bInStreamDisconnected || !m_xInStream.is() )
+ dispose();
+}
+
+void SAL_CALL OWriteStream::seek( sal_Int64 location )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xSeekable.is() )
+ throw uno::RuntimeException();
+
+ m_xSeekable->seek( location );
+}
+
+sal_Int64 SAL_CALL OWriteStream::getPosition()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xSeekable.is() )
+ throw uno::RuntimeException();
+
+ return m_xSeekable->getPosition();
+}
+
+sal_Int64 SAL_CALL OWriteStream::getLength()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xSeekable.is() )
+ throw uno::RuntimeException();
+
+ return m_xSeekable->getLength();
+}
+
+void SAL_CALL OWriteStream::truncate()
+{
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_xOutStream.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< io::XTruncate > xTruncate( m_xOutStream, uno::UNO_QUERY_THROW );
+ xTruncate->truncate();
+
+ m_pImpl->m_bHasDataToFlush = true;
+
+ ModifyParentUnlockMutex_Impl( aGuard );
+}
+
+void SAL_CALL OWriteStream::dispose()
+{
+ // should be an internal method since it can be called only from parent storage
+ {
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_xOutStream.is() )
+ CloseOutput_Impl();
+
+ if ( m_xInStream.is() )
+ {
+ m_xInStream->closeInput();
+ m_xInStream.clear();
+ }
+
+ m_xSeekable.clear();
+
+ m_pImpl->m_pAntiImpl = nullptr;
+
+ if ( !m_bInitOnDemand )
+ {
+ try
+ {
+ if ( !m_bTransacted )
+ {
+ m_pImpl->Commit();
+ }
+ else
+ {
+ // throw away all the changes
+ m_pImpl->Revert();
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+ throw lang::WrappedTargetRuntimeException("Can not commit/revert the storage!",
+ getXWeak(),
+ aCaught );
+ }
+ }
+
+ m_pImpl = nullptr;
+ }
+
+ // the listener might try to get rid of parent storage, and the storage would delete this object;
+ // for now the listener is just notified at the end of the method to workaround the problem
+ // in future a more elegant way should be found
+
+ lang::EventObject aSource( getXWeak() );
+ m_aListenersContainer.disposeAndClear( aSource );
+}
+
+void SAL_CALL OWriteStream::addEventListener(
+ const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ m_aListenersContainer.addInterface( cppu::UnoType<lang::XEventListener>::get(),
+ xListener );
+}
+
+void SAL_CALL OWriteStream::removeEventListener(
+ const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ m_aListenersContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(),
+ xListener );
+}
+
+void SAL_CALL OWriteStream::setEncryptionPassword( const OUString& aPass )
+{
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ OSL_ENSURE( m_pImpl->m_xPackageStream.is(), "No package stream is set!" );
+
+ m_pImpl->SetEncrypted( ::comphelper::OStorageHelper::CreatePackageEncryptionData( aPass ) );
+
+ ModifyParentUnlockMutex_Impl( aGuard );
+}
+
+void SAL_CALL OWriteStream::removeEncryption()
+{
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ OSL_ENSURE( m_pImpl->m_xPackageStream.is(), "No package stream is set!" );
+
+ m_pImpl->SetDecrypted();
+
+ ModifyParentUnlockMutex_Impl( aGuard );
+}
+
+void SAL_CALL OWriteStream::setEncryptionData( const uno::Sequence< beans::NamedValue >& aEncryptionData )
+{
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ CheckInitOnDemand();
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ OSL_ENSURE( m_pImpl->m_xPackageStream.is(), "No package stream is set!" );
+
+ m_pImpl->SetEncrypted( aEncryptionData );
+
+ ModifyParentUnlockMutex_Impl( aGuard );
+}
+
+sal_Bool SAL_CALL OWriteStream::hasEncryptionData()
+{
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if (!m_pImpl)
+ return false;
+
+ bool bRet = false;
+
+ try
+ {
+ bRet = m_pImpl->IsEncrypted();
+
+ if (!bRet && m_pImpl->m_bUseCommonEncryption && m_pImpl->m_pParent)
+ bRet = m_pImpl->m_pParent->m_bHasCommonEncryptionData;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+ throw lang::WrappedTargetRuntimeException( "Problems on hasEncryptionData!",
+ getXWeak(),
+ aCaught );
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL OWriteStream::hasByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ try
+ {
+ getRelationshipByID( sID );
+ return true;
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "No Element");
+ }
+
+ return false;
+}
+
+OUString SAL_CALL OWriteStream::getTargetByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID );
+ auto pRel = lcl_findPairByName(aSeq, "Target");
+ if (pRel != aSeq.end())
+ return pRel->Second;
+
+ return OUString();
+}
+
+OUString SAL_CALL OWriteStream::getTypeByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID );
+ auto pRel = lcl_findPairByName(aSeq, "Type");
+ if (pRel != aSeq.end())
+ return pRel->Second;
+
+ return OUString();
+}
+
+uno::Sequence< beans::StringPair > SAL_CALL OWriteStream::getRelationshipByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ const beans::StringPair aIDRel("Id", sID);
+ auto pRel = std::find_if(aSeq.begin(), aSeq.end(),
+ [&aIDRel](const uno::Sequence<beans::StringPair>& rRel) {
+ return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); });
+ if (pRel != aSeq.end())
+ return *pRel;
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OWriteStream::getRelationshipsByType( const OUString& sType )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ const beans::StringPair aTypeRel("Type", sType);
+ std::vector< uno::Sequence<beans::StringPair> > aResult;
+ aResult.reserve(aSeq.getLength());
+
+ std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResult),
+ [&aTypeRel](const uno::Sequence<beans::StringPair>& rRel) {
+ return std::find(rRel.begin(), rRel.end(), aTypeRel) != rRel.end(); });
+
+ return comphelper::containerToSequence(aResult);
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OWriteStream::getAllRelationships()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ return m_pImpl->GetAllRelationshipsIfAny();
+}
+
+void SAL_CALL OWriteStream::insertRelationshipByID( const OUString& sID, const uno::Sequence< beans::StringPair >& aEntry, sal_Bool bReplace )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ const beans::StringPair aIDRel("Id", sID);
+
+ uno::Sequence<beans::StringPair>* pPair = nullptr;
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ for ( sal_Int32 nInd = 0; nInd < aSeq.getLength(); nInd++ )
+ {
+ const auto& rRel = aSeq[nInd];
+ if (std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end())
+ pPair = &aSeq.getArray()[nInd];
+ }
+
+ if ( pPair && !bReplace )
+ throw container::ElementExistException(); // TODO
+
+ if ( !pPair )
+ {
+ sal_Int32 nIDInd = aSeq.getLength();
+ aSeq.realloc( nIDInd + 1 );
+ pPair = &aSeq.getArray()[nIDInd];
+ }
+
+ std::vector<beans::StringPair> aResult;
+ aResult.reserve(aEntry.getLength() + 1);
+
+ aResult.push_back(aIDRel);
+ std::copy_if(aEntry.begin(), aEntry.end(), std::back_inserter(aResult),
+ [](const beans::StringPair& rRel) { return rRel.First != "Id"; });
+
+ *pPair = comphelper::containerToSequence(aResult);
+
+ m_pImpl->m_aNewRelInfo = aSeq;
+ m_pImpl->m_xNewRelInfoStream.clear();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED;
+}
+
+void SAL_CALL OWriteStream::removeRelationshipByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ const beans::StringPair aIDRel("Id", sID);
+ auto pRel = std::find_if(std::cbegin(aSeq), std::cend(aSeq),
+ [&aIDRel](const uno::Sequence< beans::StringPair >& rRel) {
+ return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); });
+ if (pRel != std::cend(aSeq))
+ {
+ auto nInd = static_cast<sal_Int32>(std::distance(std::cbegin(aSeq), pRel));
+ comphelper::removeElementAt(aSeq, nInd);
+
+ m_pImpl->m_aNewRelInfo = aSeq;
+ m_pImpl->m_xNewRelInfoStream.clear();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED;
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ return;
+ }
+
+ throw container::NoSuchElementException();
+}
+
+void SAL_CALL OWriteStream::insertRelationships( const uno::Sequence< uno::Sequence< beans::StringPair > >& aEntries, sal_Bool bReplace )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ OUString aIDTag( "Id" );
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ std::vector< uno::Sequence<beans::StringPair> > aResultVec;
+ aResultVec.reserve(aSeq.getLength() + aEntries.getLength());
+
+ std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResultVec),
+ [&aIDTag, &aEntries, bReplace](const uno::Sequence<beans::StringPair>& rTargetRel) {
+ auto pTargetPair = lcl_findPairByName(rTargetRel, aIDTag);
+ if (pTargetPair == rTargetRel.end())
+ return false;
+
+ bool bIsSourceSame = std::any_of(aEntries.begin(), aEntries.end(),
+ [&pTargetPair](const uno::Sequence<beans::StringPair>& rSourceEntry) {
+ return std::find(rSourceEntry.begin(), rSourceEntry.end(), *pTargetPair) != rSourceEntry.end(); });
+
+ if ( bIsSourceSame && !bReplace )
+ throw container::ElementExistException();
+
+ // if no such element in the provided sequence
+ return !bIsSourceSame;
+ });
+
+ std::transform(aEntries.begin(), aEntries.end(), std::back_inserter(aResultVec),
+ [&aIDTag](const uno::Sequence<beans::StringPair>& rEntry) -> uno::Sequence<beans::StringPair> {
+ auto pPair = lcl_findPairByName(rEntry, aIDTag);
+ if (pPair == rEntry.end())
+ throw io::IOException(); // TODO: illegal relation ( no ID )
+
+ auto aResult = comphelper::sequenceToContainer<std::vector<beans::StringPair>>(rEntry);
+ auto nIDInd = std::distance(rEntry.begin(), pPair);
+ std::rotate(aResult.begin(), std::next(aResult.begin(), nIDInd), std::next(aResult.begin(), nIDInd + 1));
+
+ return comphelper::containerToSequence(aResult);
+ });
+
+ m_pImpl->m_aNewRelInfo = comphelper::containerToSequence(aResultVec);
+ m_pImpl->m_xNewRelInfoStream.clear();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED;
+}
+
+void SAL_CALL OWriteStream::clearRelationships()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException();
+
+ m_pImpl->m_aNewRelInfo.realloc( 0 );
+ m_pImpl->m_xNewRelInfoStream.clear();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED;
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL OWriteStream::getPropertySetInfo()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ //TODO:
+ return uno::Reference< beans::XPropertySetInfo >();
+}
+
+void SAL_CALL OWriteStream::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ m_pImpl->GetStreamProperties();
+ static constexpr OUString aCompressedString( u"Compressed"_ustr );
+ static constexpr OUString aMediaTypeString( u"MediaType"_ustr );
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE && aPropertyName == aMediaTypeString )
+ {
+ // if the "Compressed" property is not set explicitly, the MediaType can change the default value
+ bool bCompressedValueFromType = true;
+ OUString aType;
+ aValue >>= aType;
+
+ if ( !m_pImpl->m_bCompressedSetExplicit )
+ {
+ if ( aType == "image/jpeg" || aType == "image/png" || aType == "image/gif" )
+ bCompressedValueFromType = false;
+ }
+
+ for ( auto& rProp : asNonConstRange(m_pImpl->m_aProps) )
+ {
+ if ( aPropertyName == rProp.Name )
+ rProp.Value = aValue;
+ else if ( !m_pImpl->m_bCompressedSetExplicit && aCompressedString == rProp.Name )
+ rProp.Value <<= bCompressedValueFromType;
+ }
+ }
+ else if ( aPropertyName == aCompressedString )
+ {
+ // if the "Compressed" property is not set explicitly, the MediaType can change the default value
+ m_pImpl->m_bCompressedSetExplicit = true;
+ for ( auto& rProp : asNonConstRange(m_pImpl->m_aProps) )
+ {
+ if ( aPropertyName == rProp.Name )
+ rProp.Value = aValue;
+ }
+ }
+ else if ( m_nStorageType == embed::StorageFormats::PACKAGE
+ && aPropertyName == "UseCommonStoragePasswordEncryption" )
+ {
+ bool bUseCommonEncryption = false;
+ if ( !(aValue >>= bUseCommonEncryption) )
+ throw lang::IllegalArgumentException(); //TODO
+
+ if ( m_bInitOnDemand && m_pImpl->m_bHasInsertedStreamOptimization )
+ {
+ // the data stream is provided to the packagestream directly
+ m_pImpl->m_bUseCommonEncryption = bUseCommonEncryption;
+ }
+ else if ( bUseCommonEncryption )
+ {
+ if ( !m_pImpl->m_bUseCommonEncryption )
+ {
+ m_pImpl->SetDecrypted();
+ m_pImpl->m_bUseCommonEncryption = true;
+ }
+ }
+ else
+ m_pImpl->m_bUseCommonEncryption = false;
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML && aPropertyName == aMediaTypeString )
+ {
+ for ( auto& rProp : asNonConstRange(m_pImpl->m_aProps) )
+ {
+ if ( aPropertyName == rProp.Name )
+ rProp.Value = aValue;
+ }
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML && aPropertyName == "RelationsInfoStream" )
+ {
+ uno::Reference< io::XInputStream > xInRelStream;
+ if ( !( aValue >>= xInRelStream ) || !xInRelStream.is() )
+ throw lang::IllegalArgumentException(); // TODO
+
+ uno::Reference< io::XSeekable > xSeek( xInRelStream, uno::UNO_QUERY );
+ if ( !xSeek.is() )
+ {
+ // currently this is an internal property that is used for optimization
+ // and the stream must support XSeekable interface
+ // TODO/LATER: in future it can be changed if property is used from outside
+ throw lang::IllegalArgumentException(); // TODO
+ }
+
+ m_pImpl->m_xNewRelInfoStream = xInRelStream;
+ m_pImpl->m_aNewRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED_STREAM;
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML && aPropertyName == "RelationsInfo" )
+ {
+ if ( !(aValue >>= m_pImpl->m_aNewRelInfo) )
+ throw lang::IllegalArgumentException(); // TODO
+ }
+ else if ( aPropertyName == "Size" )
+ throw beans::PropertyVetoException(); // TODO
+ else if ( m_nStorageType == embed::StorageFormats::PACKAGE
+ && ( aPropertyName == "IsEncrypted" || aPropertyName == "Encrypted" ) )
+ throw beans::PropertyVetoException(); // TODO
+ else if ( aPropertyName == "RelId" )
+ {
+ aValue >>= m_pImpl->m_nRelId;
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName); // TODO
+
+ m_pImpl->m_bHasDataToFlush = true;
+ ModifyParentUnlockMutex_Impl( aGuard );
+}
+
+uno::Any SAL_CALL OWriteStream::getPropertyValue( const OUString& aProp )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( aProp == "RelId" )
+ {
+ return uno::Any( m_pImpl->GetNewRelId() );
+ }
+
+ OUString aPropertyName;
+ if ( aProp == "IsEncrypted" )
+ aPropertyName = "Encrypted";
+ else
+ aPropertyName = aProp;
+
+ if ( ( ( m_nStorageType == embed::StorageFormats::PACKAGE || m_nStorageType == embed::StorageFormats::OFOPXML )
+ && aPropertyName == "MediaType" )
+ || ( m_nStorageType == embed::StorageFormats::PACKAGE && aPropertyName == "Encrypted" )
+ || aPropertyName == "Compressed" )
+ {
+ m_pImpl->GetStreamProperties();
+
+ auto pProp = std::find_if(std::cbegin(m_pImpl->m_aProps), std::cend(m_pImpl->m_aProps),
+ [&aPropertyName](const css::beans::PropertyValue& rProp){ return aPropertyName == rProp.Name; });
+ if (pProp != std::cend(m_pImpl->m_aProps))
+ return pProp->Value;
+ }
+ else if ( m_nStorageType == embed::StorageFormats::PACKAGE
+ && aPropertyName == "UseCommonStoragePasswordEncryption" )
+ return uno::Any( m_pImpl->m_bUseCommonEncryption );
+ else if ( aPropertyName == "Size" )
+ {
+ bool bThrow = false;
+ try
+ {
+ CheckInitOnDemand();
+ }
+ catch (const io::IOException&)
+ {
+ bThrow = true;
+ }
+ if (bThrow || !m_xSeekable.is())
+ throw uno::RuntimeException();
+
+ return uno::Any( m_xSeekable->getLength() );
+ }
+
+ throw beans::UnknownPropertyException(aPropertyName); // TODO
+}
+
+void SAL_CALL OWriteStream::addPropertyChangeListener(
+ const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ //TODO:
+}
+
+void SAL_CALL OWriteStream::removePropertyChangeListener(
+ const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ //TODO:
+}
+
+void SAL_CALL OWriteStream::addVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ //TODO:
+}
+
+void SAL_CALL OWriteStream::removeVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ //TODO:
+}
+
+// XTransactedObject
+
+void OWriteStream::BroadcastTransaction( sal_Int8 nMessage )
+/*
+ 1 - preCommit
+ 2 - committed
+ 3 - preRevert
+ 4 - reverted
+*/
+{
+ // no need to lock mutex here for the checking of m_pImpl, and m_pData is alive until the object is destructed
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ lang::EventObject aSource( getXWeak() );
+
+ comphelper::OInterfaceContainerHelper2* pContainer =
+ m_aListenersContainer.getContainer(
+ cppu::UnoType<embed::XTransactionListener>::get());
+ if ( !pContainer )
+ return;
+
+ comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer );
+ while ( pIterator.hasMoreElements( ) )
+ {
+ OSL_ENSURE( nMessage >= 1 && nMessage <= 4, "Wrong internal notification code is used!" );
+
+ switch( nMessage )
+ {
+ case STOR_MESS_PRECOMMIT:
+ static_cast<embed::XTransactionListener*>( pIterator.next( ) )->preCommit( aSource );
+ break;
+ case STOR_MESS_COMMITTED:
+ static_cast<embed::XTransactionListener*>( pIterator.next( ) )->commited( aSource );
+ break;
+ case STOR_MESS_PREREVERT:
+ static_cast<embed::XTransactionListener*>( pIterator.next( ) )->preRevert( aSource );
+ break;
+ case STOR_MESS_REVERTED:
+ static_cast< embed::XTransactionListener*>( pIterator.next( ) )->reverted( aSource );
+ break;
+ }
+ }
+}
+void SAL_CALL OWriteStream::commit()
+{
+ SAL_INFO( "package.xstor", "package (mv76033) OWriteStream::commit" );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bTransacted )
+ throw uno::RuntimeException();
+
+ try {
+ BroadcastTransaction( STOR_MESS_PRECOMMIT );
+
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ m_pImpl->Commit();
+
+ // when the storage is committed the parent is modified
+ ModifyParentUnlockMutex_Impl( aGuard );
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+ throw embed::StorageWrappedTargetException( "Problems on commit!",
+ getXWeak(),
+ aCaught );
+ }
+
+ BroadcastTransaction( STOR_MESS_COMMITTED );
+}
+
+void SAL_CALL OWriteStream::revert()
+{
+ SAL_INFO( "package.xstor", "package (mv76033) OWriteStream::revert" );
+
+ // the method removes all the changes done after last commit
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bTransacted )
+ throw uno::RuntimeException();
+
+ BroadcastTransaction( STOR_MESS_PREREVERT );
+
+ {
+ osl::MutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if (!m_pImpl)
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ try {
+ m_pImpl->Revert();
+ }
+ catch (const io::IOException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const embed::StorageWrappedTargetException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ uno::Any aCaught(::cppu::getCaughtException());
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+ throw embed::StorageWrappedTargetException("Problems on revert!",
+ getXWeak(),
+ aCaught);
+ }
+ }
+
+ BroadcastTransaction( STOR_MESS_REVERTED );
+}
+
+// XTransactionBroadcaster
+
+void SAL_CALL OWriteStream::addTransactionListener( const uno::Reference< embed::XTransactionListener >& aListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bTransacted )
+ throw uno::RuntimeException();
+
+ m_aListenersContainer.addInterface( cppu::UnoType<embed::XTransactionListener>::get(),
+ aListener );
+}
+
+void SAL_CALL OWriteStream::removeTransactionListener( const uno::Reference< embed::XTransactionListener >& aListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", "Disposed!");
+ throw lang::DisposedException();
+ }
+
+ if ( !m_bTransacted )
+ throw uno::RuntimeException();
+
+ m_aListenersContainer.removeInterface( cppu::UnoType<embed::XTransactionListener>::get(),
+ aListener );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/owriteablestream.hxx b/package/source/xstor/owriteablestream.hxx
new file mode 100644
index 0000000000..e04b50c993
--- /dev/null
+++ b/package/source/xstor/owriteablestream.hxx
@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_OWRITEABLESTREAM_HXX
+#define INCLUDED_PACKAGE_SOURCE_XSTOR_OWRITEABLESTREAM_HXX
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/packages/XDataSinkEncrSupport.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/embed/XEncryptionProtectedSource2.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/XRelationshipAccess.hpp>
+#include <com/sun/star/embed/XExtendedStorageStream.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/XTransactionBroadcaster.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <comphelper/multicontainer2.hxx>
+
+#include <comphelper/bytereader.hxx>
+#include <comphelper/refcountedmutex.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <vector>
+#include <memory>
+#include <string_view>
+
+#include "ocompinstream.hxx"
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+namespace package {
+ // all data in aHash1 is contained in aHash2
+ bool PackageEncryptionDataLessOrEqual( const ::comphelper::SequenceAsHashMap& aHash1, const ::comphelper::SequenceAsHashMap& aHash2 );
+}
+
+struct OStorage_Impl;
+class OWriteStream;
+
+struct OWriteStream_Impl
+{
+ rtl::Reference<comphelper::RefCountedMutex> m_xMutex;
+
+ friend struct OStorage_Impl;
+ friend class OWriteStream;
+ friend class OInputCompStream;
+
+ OWriteStream* m_pAntiImpl;
+ std::optional<utl::TempFileFast> m_oTempFile;
+
+ css::uno::Reference< css::io::XStream > m_xCacheStream;
+ css::uno::Reference< css::io::XSeekable > m_xCacheSeek;
+
+ std::vector< OInputCompStream* > m_aInputStreamsVector;
+
+ bool m_bHasDataToFlush; // only modified elements will be sent to the original content
+ bool m_bFlushed; // sending the streams is coordinated by the root storage of the package
+
+ css::uno::Reference< css::packages::XDataSinkEncrSupport > m_xPackageStream;
+
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ OStorage_Impl* m_pParent;
+
+ css::uno::Sequence< css::beans::PropertyValue > m_aProps;
+
+ bool m_bForceEncrypted;
+
+ bool m_bUseCommonEncryption;
+ bool m_bHasCachedEncryptionData;
+ ::comphelper::SequenceAsHashMap m_aEncryptionData;
+
+ bool m_bCompressedSetExplicit;
+
+ css::uno::Reference< css::lang::XSingleServiceFactory > m_xPackage;
+
+ bool m_bHasInsertedStreamOptimization;
+
+ sal_Int32 m_nStorageType;
+
+ // Relations info related data, stored in *.rels file in OFOPXML format
+ css::uno::Reference< css::io::XInputStream > m_xOrigRelInfoStream;
+ css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aOrigRelInfo;
+ bool m_bOrigRelInfoBroken;
+
+ css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aNewRelInfo;
+ css::uno::Reference< css::io::XInputStream > m_xNewRelInfoStream;
+ sal_Int16 m_nRelInfoStatus;
+ sal_Int32 m_nRelId;
+
+private:
+ void GetFilledTempFileIfNo( const css::uno::Reference< css::io::XInputStream >& xStream );
+ void FillTempGetFileName();
+ css::uno::Reference< css::io::XStream > GetTempFileAsStream();
+ css::uno::Reference< css::io::XInputStream > GetTempFileAsInputStream();
+
+ css::uno::Reference< css::io::XStream > GetStream_Impl( sal_Int32 nStreamMode,
+ bool bHierarchyAccess );
+
+ /// @throws css::packages::NoEncryptionException
+ ::comphelper::SequenceAsHashMap GetCommonRootEncryptionData();
+
+ css::uno::Sequence< css::beans::PropertyValue > ReadPackageStreamProperties();
+ css::uno::Sequence< css::beans::PropertyValue > InsertOwnProps(
+ const css::uno::Sequence< css::beans::PropertyValue >& aProps,
+ bool bUseCommonEncryption );
+
+public:
+ OWriteStream_Impl(
+ OStorage_Impl* pParent,
+ const css::uno::Reference< css::packages::XDataSinkEncrSupport >& xPackageStream,
+ const css::uno::Reference< css::lang::XSingleServiceFactory >& xPackage,
+ css::uno::Reference< css::uno::XComponentContext > xContext,
+ bool bForceEncrypted,
+ sal_Int32 nStorageType,
+ bool bDefaultCompress,
+ css::uno::Reference< css::io::XInputStream > xRelInfoStream =
+ css::uno::Reference< css::io::XInputStream >() );
+
+ ~OWriteStream_Impl();
+
+ void CleanCacheStream();
+
+ bool UsesCommonEncryption_Impl() const { return m_bUseCommonEncryption; }
+ bool HasTempFile_Impl() const { return m_oTempFile.has_value(); }
+ bool IsTransacted();
+
+ bool HasWriteOwner_Impl() const { return ( m_pAntiImpl != nullptr ); }
+
+ void InsertIntoPackageFolder(
+ const OUString& aName,
+ const css::uno::Reference< css::container::XNameContainer >& xParentPackageFolder );
+
+ void SetToBeCommited() { m_bFlushed = true; }
+
+ bool HasCachedEncryptionData() const { return m_bHasCachedEncryptionData; }
+ ::comphelper::SequenceAsHashMap& GetCachedEncryptionData() { return m_aEncryptionData; }
+
+ bool IsModified() const { return m_bHasDataToFlush || m_bFlushed; }
+
+ bool IsEncrypted();
+ void SetDecrypted();
+ void SetEncrypted( const ::comphelper::SequenceAsHashMap& aEncryptionData );
+
+ void DisposeWrappers();
+
+ void InsertStreamDirectly(
+ const css::uno::Reference< css::io::XInputStream >& xInStream,
+ const css::uno::Sequence< css::beans::PropertyValue >& aProps );
+
+ void Commit();
+ void Revert();
+
+ css::uno::Sequence< css::beans::PropertyValue > const & GetStreamProperties();
+
+ css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > GetAllRelationshipsIfAny();
+
+ void CopyInternallyTo_Impl( const css::uno::Reference< css::io::XStream >& xDestStream,
+ const ::comphelper::SequenceAsHashMap& aEncryptionData );
+ void CopyInternallyTo_Impl( const css::uno::Reference< css::io::XStream >& xDestStream );
+
+ css::uno::Reference< css::io::XStream > GetStream(
+ sal_Int32 nStreamMode,
+ const ::comphelper::SequenceAsHashMap& aEncryptionData,
+ bool bHierarchyAccess );
+
+ css::uno::Reference< css::io::XStream > GetStream(
+ sal_Int32 nStreamMode,
+ bool bHierarchyAccess );
+
+ css::uno::Reference< css::io::XInputStream > GetRawInStream();
+ css::uno::Reference< css::io::XInputStream > GetPlainRawInStream();
+
+ void InputStreamDisposed( OInputCompStream* pStream );
+
+ void CreateReadonlyCopyBasedOnData(
+ const css::uno::Reference< css::io::XInputStream >& xDataToCopy,
+ const css::uno::Sequence< css::beans::PropertyValue >& aProps,
+ css::uno::Reference< css::io::XStream >& xTargetStream );
+
+ void GetCopyOfLastCommit( css::uno::Reference< css::io::XStream >& xTargetStream );
+ void GetCopyOfLastCommit(
+ css::uno::Reference< css::io::XStream >& xTargetStream,
+ const ::comphelper::SequenceAsHashMap& aEncryptionData );
+
+ void CommitStreamRelInfo(
+ const css::uno::Reference< css::embed::XStorage >& xRelStorage,
+ std::u16string_view aOrigStreamName,
+ std::u16string_view aNewStreamName );
+
+ void ReadRelInfoIfNecessary();
+
+ sal_Int32 GetNewRelId() { return m_nRelId ++; }
+};
+
+class OWriteStream : public css::lang::XTypeProvider
+ , public css::io::XInputStream
+ , public css::io::XOutputStream
+ , public css::embed::XExtendedStorageStream
+ , public css::io::XSeekable
+ , public css::io::XTruncate
+ , public css::embed::XEncryptionProtectedSource2
+ , public css::embed::XRelationshipAccess
+ , public css::embed::XTransactedObject
+ , public css::embed::XTransactionBroadcaster
+ , public css::beans::XPropertySet
+ , public ::cppu::OWeakObject
+ , public comphelper::ByteWriter
+{
+ friend struct OWriteStream_Impl;
+
+protected:
+ css::uno::Reference < css::io::XInputStream > m_xInStream;
+ css::uno::Reference < css::io::XOutputStream > m_xOutStream;
+ css::uno::Reference < css::io::XSeekable > m_xSeekable;
+
+ OWriteStream_Impl* m_pImpl;
+ rtl::Reference<comphelper::RefCountedMutex> m_xSharedMutex;
+ ::std::optional< ::cppu::OTypeCollection> m_oTypeCollection;
+ comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenersContainer; // list of listeners
+ sal_Int32 m_nStorageType;
+
+ bool m_bInStreamDisconnected;
+ bool m_bInitOnDemand;
+ sal_Int64 m_nInitPosition;
+
+ bool m_bTransacted;
+
+ OWriteStream( OWriteStream_Impl& rImpl, bool bTransacted );
+ OWriteStream( OWriteStream_Impl& rImpl, css::uno::Reference< css::io::XStream > const & xStream, bool bTransacted );
+
+ void CloseOutput_Impl();
+
+ void CopyToStreamInternally_Impl( const css::uno::Reference< css::io::XStream >& xStream );
+
+ void ModifyParentUnlockMutex_Impl(osl::ClearableMutexGuard& aGuard);
+
+ void BroadcastTransaction( sal_Int8 nMessage );
+
+public:
+
+ virtual ~OWriteStream() override;
+
+ void CheckInitOnDemand();
+ void DeInit();
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+
+ // XOutputStream
+ virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override;
+ virtual void SAL_CALL flush( ) override;
+ virtual void SAL_CALL closeOutput( ) override;
+
+ //XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+ virtual sal_Int64 SAL_CALL getPosition() override;
+ virtual sal_Int64 SAL_CALL getLength() override;
+
+ //XStream
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getInputStream( ) override;
+ virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL getOutputStream( ) override;
+
+ // XTruncate
+ virtual void SAL_CALL truncate() override;
+
+ //XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ //XEncryptionProtectedSource
+ virtual void SAL_CALL setEncryptionPassword( const OUString& aPass ) override;
+ virtual void SAL_CALL removeEncryption() override;
+
+ //XEncryptionProtectedSource2
+ virtual void SAL_CALL setEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override;
+ virtual sal_Bool SAL_CALL hasEncryptionData() override;
+
+ //XRelationshipAccess
+ virtual sal_Bool SAL_CALL hasByID( const OUString& sID ) override;
+ virtual OUString SAL_CALL getTargetByID( const OUString& sID ) override;
+ virtual OUString SAL_CALL getTypeByID( const OUString& sID ) override;
+ virtual css::uno::Sequence< css::beans::StringPair > SAL_CALL getRelationshipByID( const OUString& sID ) override;
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getRelationshipsByType( const OUString& sType ) override;
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getAllRelationships( ) override;
+ virtual void SAL_CALL insertRelationshipByID( const OUString& sID, const css::uno::Sequence< css::beans::StringPair >& aEntry, sal_Bool bReplace ) override;
+ virtual void SAL_CALL removeRelationshipByID( const OUString& sID ) override;
+ virtual void SAL_CALL insertRelationships( const css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > >& aEntries, sal_Bool bReplace ) override;
+ virtual void SAL_CALL clearRelationships( ) override;
+
+ //XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ // XTransactedObject
+ virtual void SAL_CALL commit() override;
+ virtual void SAL_CALL revert() override;
+
+ // XTransactionBroadcaster
+ virtual void SAL_CALL addTransactionListener(
+ const css::uno::Reference< css::embed::XTransactionListener >& aListener ) override;
+ virtual void SAL_CALL removeTransactionListener(
+ const css::uno::Reference< css::embed::XTransactionListener >& aListener ) override;
+
+ // comphelper::ByteWriter
+ virtual void writeBytes(const sal_Int8* aData, sal_Int32 nBytesToWrite) override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/selfterminatefilestream.cxx b/package/source/xstor/selfterminatefilestream.cxx
new file mode 100644
index 0000000000..f3fe794397
--- /dev/null
+++ b/package/source/xstor/selfterminatefilestream.cxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+
+#include "selfterminatefilestream.hxx"
+#include <comphelper/processfactory.hxx>
+#include <unotools/streamwrap.hxx>
+
+using namespace ::com::sun::star;
+
+OSelfTerminateFileStream::OSelfTerminateFileStream( const uno::Reference< uno::XComponentContext >& xContext, utl::TempFileFast aTempFile )
+: m_oTempFile( std::move(aTempFile) )
+{
+ uno::Reference< uno::XComponentContext > xOwnContext = xContext;
+ if ( !xOwnContext.is() )
+ xOwnContext.set( ::comphelper::getProcessComponentContext(), uno::UNO_SET_THROW );
+
+ m_xStreamWrapper = new utl::OSeekableInputStreamWrapper( m_oTempFile->GetStream(StreamMode::READWRITE), /*bOwner*/false );
+}
+
+OSelfTerminateFileStream::~OSelfTerminateFileStream()
+{
+ CloseStreamDeleteFile();
+}
+
+void OSelfTerminateFileStream::CloseStreamDeleteFile()
+{
+ try
+ {
+ m_xStreamWrapper->closeInput();
+ }
+ catch( uno::Exception& )
+ {}
+
+ m_oTempFile.reset();
+}
+
+sal_Int32 SAL_CALL OSelfTerminateFileStream::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ return m_xStreamWrapper->readBytes( aData, nBytesToRead );
+}
+
+sal_Int32 SAL_CALL OSelfTerminateFileStream::readSomeBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ return m_xStreamWrapper->readSomeBytes( aData, nMaxBytesToRead );
+}
+
+void SAL_CALL OSelfTerminateFileStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ return m_xStreamWrapper->skipBytes( nBytesToSkip );
+}
+
+sal_Int32 SAL_CALL OSelfTerminateFileStream::available( )
+{
+ return m_xStreamWrapper->available();
+}
+
+void SAL_CALL OSelfTerminateFileStream::closeInput( )
+{
+ CloseStreamDeleteFile();
+}
+
+void SAL_CALL OSelfTerminateFileStream::seek( sal_Int64 location )
+{
+ m_xStreamWrapper->seek( location );
+}
+
+sal_Int64 SAL_CALL OSelfTerminateFileStream::getPosition()
+{
+ return m_xStreamWrapper->getPosition();
+}
+
+sal_Int64 SAL_CALL OSelfTerminateFileStream::getLength()
+{
+ return m_xStreamWrapper->getLength();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/selfterminatefilestream.hxx b/package/source/xstor/selfterminatefilestream.hxx
new file mode 100644
index 0000000000..f8cedcb64c
--- /dev/null
+++ b/package/source/xstor/selfterminatefilestream.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_SELFTERMINATEFILESTREAM_HXX
+#define INCLUDED_PACKAGE_SOURCE_XSTOR_SELFTERMINATEFILESTREAM_HXX
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
+#include <rtl/ref.hxx>
+#include <optional>
+
+struct OWriteStream_Impl;
+
+class OSelfTerminateFileStream final : public cppu::WeakImplHelper< css::io::XInputStream,
+ css::io::XSeekable >
+{
+ std::optional<utl::TempFileFast> m_oTempFile;
+ rtl::Reference< utl::OSeekableInputStreamWrapper > m_xStreamWrapper;
+
+public:
+ OSelfTerminateFileStream( const css::uno::Reference< css::uno::XComponentContext >& xContext, utl::TempFileFast aTempFile );
+
+ virtual ~OSelfTerminateFileStream() override;
+
+ void CloseStreamDeleteFile();
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available() override;
+ virtual void SAL_CALL closeInput() override;
+
+ //XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+ virtual sal_Int64 SAL_CALL getPosition() override;
+ virtual sal_Int64 SAL_CALL getLength() override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/switchpersistencestream.cxx b/package/source/xstor/switchpersistencestream.cxx
new file mode 100644
index 0000000000..ff61187266
--- /dev/null
+++ b/package/source/xstor/switchpersistencestream.cxx
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <comphelper/storagehelper.hxx>
+#include <unotools/tempfile.hxx>
+#include <utility>
+#include "switchpersistencestream.hxx"
+
+using namespace ::com::sun::star;
+
+struct SPStreamData_Impl
+{
+ bool m_bInStreamBased;
+
+ // the streams below are not visible from outside so there is no need to remember position
+
+ // original stream related members
+ uno::Reference< io::XTruncate > m_xOrigTruncate;
+ uno::Reference< io::XSeekable > m_xOrigSeekable;
+ uno::Reference< io::XInputStream > m_xOrigInStream;
+ uno::Reference< io::XOutputStream > m_xOrigOutStream;
+
+ bool m_bInOpen;
+ bool m_bOutOpen;
+
+ SPStreamData_Impl(
+ bool bInStreamBased,
+ uno::Reference< io::XTruncate > xOrigTruncate,
+ uno::Reference< io::XSeekable > xOrigSeekable,
+ uno::Reference< io::XInputStream > xOrigInStream,
+ uno::Reference< io::XOutputStream > xOrigOutStream,
+ bool bInOpen,
+ bool bOutOpen )
+ : m_bInStreamBased( bInStreamBased )
+ , m_xOrigTruncate(std::move( xOrigTruncate ))
+ , m_xOrigSeekable(std::move( xOrigSeekable ))
+ , m_xOrigInStream(std::move( xOrigInStream ))
+ , m_xOrigOutStream(std::move( xOrigOutStream ))
+ , m_bInOpen( bInOpen )
+ , m_bOutOpen( bOutOpen )
+ {
+ }
+};
+
+SwitchablePersistenceStream::SwitchablePersistenceStream(
+ const uno::Reference< io::XStream >& xStream )
+{
+ SwitchPersistenceTo( xStream );
+}
+
+SwitchablePersistenceStream::SwitchablePersistenceStream(
+ const uno::Reference< io::XInputStream >& xInputStream )
+{
+ SwitchPersistenceTo( xInputStream );
+}
+
+SwitchablePersistenceStream::~SwitchablePersistenceStream()
+{
+ CloseAll_Impl();
+}
+
+void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XStream >& xStream )
+{
+ uno::Reference< io::XTruncate > xNewTruncate( xStream, uno::UNO_QUERY_THROW );
+ uno::Reference< io::XSeekable > xNewSeekable( xStream, uno::UNO_QUERY_THROW );
+ uno::Reference< io::XInputStream > xNewInStream = xStream->getInputStream();
+ uno::Reference< io::XOutputStream > xNewOutStream = xStream->getOutputStream();
+ if ( !xNewInStream.is() || !xNewOutStream.is() )
+ throw uno::RuntimeException();
+
+ sal_Int64 nPos = 0;
+ bool bInOpen = false;
+ bool bOutOpen = false;
+
+ if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() )
+ {
+ // check that the length is the same
+ if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() )
+ throw uno::RuntimeException();
+
+ // get the current position
+ nPos = m_pStreamData->m_xOrigSeekable->getPosition();
+ bInOpen = m_pStreamData->m_bInOpen;
+ bOutOpen = m_pStreamData->m_bOutOpen;
+ }
+
+ xNewSeekable->seek( nPos );
+
+ CloseAll_Impl();
+
+ m_pStreamData.reset( new SPStreamData_Impl( false,
+ xNewTruncate, xNewSeekable, xNewInStream, xNewOutStream,
+ bInOpen, bOutOpen ) );
+}
+
+void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XInputStream >& xInputStream )
+{
+ uno::Reference< io::XTruncate > xNewTruncate;
+ uno::Reference< io::XSeekable > xNewSeekable( xInputStream, uno::UNO_QUERY_THROW );
+ uno::Reference< io::XOutputStream > xNewOutStream;
+ if ( !xInputStream.is() )
+ throw uno::RuntimeException();
+
+ sal_Int64 nPos = 0;
+ bool bInOpen = false;
+ bool bOutOpen = false;
+
+ if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() )
+ {
+ // check that the length is the same
+ if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() )
+ throw uno::RuntimeException();
+
+ // get the current position
+ nPos = m_pStreamData->m_xOrigSeekable->getPosition();
+ bInOpen = m_pStreamData->m_bInOpen;
+ bOutOpen = m_pStreamData->m_bOutOpen;
+ }
+
+ xNewSeekable->seek( nPos );
+
+ CloseAll_Impl();
+
+ m_pStreamData.reset( new SPStreamData_Impl( true,
+ xNewTruncate, xNewSeekable, xInputStream, xNewOutStream,
+ bInOpen, bOutOpen ) );
+
+}
+
+void SwitchablePersistenceStream::CopyAndSwitchPersistenceTo( const uno::Reference< io::XStream >& xStream )
+{
+ uno::Reference< io::XStream > xTargetStream = xStream;
+ uno::Reference< io::XSeekable > xTargetSeek;
+
+ if ( !xTargetStream.is() )
+ {
+ xTargetStream.set( new utl::TempFileFastService );
+ xTargetSeek.set( xTargetStream, uno::UNO_QUERY_THROW );
+ }
+ else
+ {
+ // the provided stream must be empty
+ xTargetSeek.set( xTargetStream, uno::UNO_QUERY_THROW );
+ if ( xTargetSeek->getLength() )
+ throw io::IOException("provided stream not empty");
+ }
+
+ uno::Reference< io::XTruncate > xTargetTruncate( xTargetStream, uno::UNO_QUERY_THROW );
+ uno::Reference< io::XInputStream > xTargetInStream = xTargetStream->getInputStream();
+ uno::Reference< io::XOutputStream > xTargetOutStream = xTargetStream->getOutputStream();
+ if ( !xTargetInStream.is() || !xTargetOutStream.is() )
+ throw uno::RuntimeException();
+
+ if ( !m_pStreamData->m_xOrigInStream.is() || !m_pStreamData->m_xOrigSeekable.is() )
+ throw uno::RuntimeException();
+
+ sal_Int64 nPos = m_pStreamData->m_xOrigSeekable->getPosition();
+ m_pStreamData->m_xOrigSeekable->seek( 0 );
+ ::comphelper::OStorageHelper::CopyInputToOutput( m_pStreamData->m_xOrigInStream, xTargetOutStream );
+ xTargetOutStream->flush();
+ xTargetSeek->seek( nPos );
+
+ bool bInOpen = m_pStreamData->m_bInOpen;
+ bool bOutOpen = m_pStreamData->m_bOutOpen;
+
+ CloseAll_Impl();
+
+ m_pStreamData.reset( new SPStreamData_Impl( false,
+ xTargetTruncate, xTargetSeek, xTargetInStream, xTargetOutStream,
+ bInOpen, bOutOpen ) );
+}
+
+void SwitchablePersistenceStream::CloseAll_Impl()
+{
+ m_pStreamData.reset();
+}
+
+// css::io::XStream
+uno::Reference< io::XInputStream > SAL_CALL SwitchablePersistenceStream::getInputStream( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( m_pStreamData )
+ m_pStreamData->m_bInOpen = true;
+ return static_cast< io::XInputStream* >( this );
+}
+
+uno::Reference< io::XOutputStream > SAL_CALL SwitchablePersistenceStream::getOutputStream( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( m_pStreamData )
+ m_pStreamData->m_bOutOpen = true;
+ return static_cast< io::XOutputStream* >( this );
+}
+
+// css::io::XInputStream
+::sal_Int32 SAL_CALL SwitchablePersistenceStream::readBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigInStream.is() )
+ throw uno::RuntimeException();
+
+ return m_pStreamData->m_xOrigInStream->readBytes( aData, nBytesToRead );
+}
+
+::sal_Int32 SAL_CALL SwitchablePersistenceStream::readSomeBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigInStream.is() )
+ throw uno::RuntimeException();
+
+ return m_pStreamData->m_xOrigInStream->readBytes( aData, nMaxBytesToRead );
+}
+
+void SAL_CALL SwitchablePersistenceStream::skipBytes( ::sal_Int32 nBytesToSkip )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigInStream.is() )
+ throw uno::RuntimeException();
+
+ m_pStreamData->m_xOrigInStream->skipBytes( nBytesToSkip );
+}
+
+::sal_Int32 SAL_CALL SwitchablePersistenceStream::available( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigInStream.is() )
+ throw uno::RuntimeException();
+
+ return m_pStreamData->m_xOrigInStream->available();
+}
+
+void SAL_CALL SwitchablePersistenceStream::closeInput()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ m_pStreamData->m_bInOpen = false;
+ if ( !m_pStreamData->m_bOutOpen )
+ CloseAll_Impl();
+}
+
+// css::io::XOutputStream
+void SAL_CALL SwitchablePersistenceStream::writeBytes( const uno::Sequence< ::sal_Int8 >& aData )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ if ( m_pStreamData->m_bInStreamBased )
+ throw io::IOException();
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigOutStream.is() )
+ throw uno::RuntimeException();
+
+ m_pStreamData->m_xOrigOutStream->writeBytes( aData );
+}
+
+void SAL_CALL SwitchablePersistenceStream::flush( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData || m_pStreamData->m_bInStreamBased )
+ {
+ OSL_FAIL( "flush() is not acceptable!" );
+ return;
+ // in future throw exception, for now some code might call flush() on closed stream
+ // since file ucp implementation allows it
+ // throw io::NotConnectedException();
+ }
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigOutStream.is() )
+ throw uno::RuntimeException();
+
+ m_pStreamData->m_xOrigOutStream->flush();
+}
+
+void SAL_CALL SwitchablePersistenceStream::closeOutput( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ m_pStreamData->m_bOutOpen = false;
+ if ( !m_pStreamData->m_bInOpen )
+ CloseAll_Impl();
+}
+
+// css::io::XTruncate
+void SAL_CALL SwitchablePersistenceStream::truncate( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ if ( m_pStreamData->m_bInStreamBased )
+ throw io::IOException();
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigTruncate.is() )
+ throw uno::RuntimeException();
+
+ m_pStreamData->m_xOrigTruncate->truncate();
+}
+
+// css::io::XSeekable
+void SAL_CALL SwitchablePersistenceStream::seek( ::sal_Int64 location )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigSeekable.is() )
+ throw uno::RuntimeException();
+
+ m_pStreamData->m_xOrigSeekable->seek( location );
+}
+
+::sal_Int64 SAL_CALL SwitchablePersistenceStream::getPosition( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigSeekable.is() )
+ throw uno::RuntimeException();
+
+ return m_pStreamData->m_xOrigSeekable->getPosition();
+}
+
+::sal_Int64 SAL_CALL SwitchablePersistenceStream::getLength( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ // the original stream data should be provided
+ if ( !m_pStreamData->m_xOrigSeekable.is() )
+ throw uno::RuntimeException();
+
+ return m_pStreamData->m_xOrigSeekable->getLength();
+}
+
+void SAL_CALL SwitchablePersistenceStream::waitForCompletion()
+{
+ if ( !m_pStreamData )
+ throw io::NotConnectedException();
+
+ uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( m_pStreamData->m_xOrigOutStream, uno::UNO_QUERY );
+ if ( asyncOutputMonitor.is() )
+ asyncOutputMonitor->waitForCompletion();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/switchpersistencestream.hxx b/package/source/xstor/switchpersistencestream.hxx
new file mode 100644
index 0000000000..64d4e37fd0
--- /dev/null
+++ b/package/source/xstor/switchpersistencestream.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_SWITCHPERSISTENCESTREAM_HXX
+#define INCLUDED_PACKAGE_SOURCE_XSTOR_SWITCHPERSISTENCESTREAM_HXX
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XAsyncOutputMonitor.hpp>
+#include <mutex>
+#include <cppuhelper/implbase.hxx>
+
+// SwitchablePersistenceStream
+
+// Allows to switch the stream persistence on the fly. The target
+// stream ( if not filled by the implementation ) MUST have the same
+// size as the original one!
+
+struct SPStreamData_Impl;
+class SwitchablePersistenceStream
+ : public ::cppu::WeakImplHelper <
+ css::io::XStream,
+ css::io::XInputStream,
+ css::io::XOutputStream,
+ css::io::XTruncate,
+ css::io::XSeekable,
+ css::io::XAsyncOutputMonitor >
+{
+ std::mutex m_aMutex;
+
+ std::unique_ptr<SPStreamData_Impl> m_pStreamData;
+
+ void CloseAll_Impl();
+
+public:
+
+ SwitchablePersistenceStream(
+ const css::uno::Reference< css::io::XStream >& xStream );
+
+ SwitchablePersistenceStream(
+ const css::uno::Reference< css::io::XInputStream >& xInStream );
+
+ virtual ~SwitchablePersistenceStream() override;
+
+ void SwitchPersistenceTo( const css::uno::Reference< css::io::XStream >& xStream );
+
+ void SwitchPersistenceTo( const css::uno::Reference< css::io::XInputStream >& xInputStream );
+
+ void CopyAndSwitchPersistenceTo( const css::uno::Reference< css::io::XStream >& xStream );
+
+// css::io::XStream
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getInputStream( ) override;
+ virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL getOutputStream( ) override;
+
+// css::io::XInputStream
+ virtual ::sal_Int32 SAL_CALL readBytes( css::uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead ) override;
+ virtual ::sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( ::sal_Int32 nBytesToSkip ) override;
+ virtual ::sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+
+// css::io::XOutputStream
+ virtual void SAL_CALL writeBytes( const css::uno::Sequence< ::sal_Int8 >& aData ) override;
+ virtual void SAL_CALL flush( ) override;
+ virtual void SAL_CALL closeOutput( ) override;
+
+// css::io::XTruncate
+ virtual void SAL_CALL truncate( ) override;
+
+// css::io::XSeekable
+ virtual void SAL_CALL seek( ::sal_Int64 location ) override;
+ virtual ::sal_Int64 SAL_CALL getPosition( ) override;
+ virtual ::sal_Int64 SAL_CALL getLength( ) override;
+
+// css::io::XAsyncOutputMonitor
+ virtual void SAL_CALL waitForCompletion( ) override;
+
+};
+
+#endif // INCLUDED_PACKAGE_SOURCE_XSTOR_SWITCHPERSISTENCESTREAM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/xfactory.cxx b/package/source/xstor/xfactory.cxx
new file mode 100644
index 0000000000..d611ddfabe
--- /dev/null
+++ b/package/source/xstor/xfactory.cxx
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <osl/diagnose.h>
+#include <unotools/tempfile.hxx>
+
+#include "xfactory.hxx"
+#include "xstorage.hxx"
+
+using namespace ::com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+static bool CheckPackageSignature_Impl( const uno::Reference< io::XInputStream >& xInputStream,
+ const uno::Reference< io::XSeekable >& xSeekable )
+{
+ if ( !xInputStream.is() || !xSeekable.is() )
+ throw uno::RuntimeException();
+
+ if ( xSeekable->getLength() )
+ {
+ uno::Sequence< sal_Int8 > aData( 4 );
+ xSeekable->seek( 0 );
+ sal_Int32 nRead = xInputStream->readBytes( aData, 4 );
+ xSeekable->seek( 0 );
+
+ // TODO/LATER: should the disk spanned files be supported?
+ // 0x50, 0x4b, 0x07, 0x08
+ return ( nRead == 4 && aData[0] == 0x50 && aData[1] == 0x4b && aData[2] == 0x03 && aData[3] == 0x04 );
+ }
+ else
+ return true; // allow to create a storage based on empty stream
+}
+
+
+
+uno::Reference< uno::XInterface > SAL_CALL OStorageFactory::createInstance()
+{
+ // TODO: reimplement TempStream service to support XStream interface
+ uno::Reference < io::XStream > xTempStream(new utl::TempFileFastService);
+
+ return cppu::getXWeak(new OStorage(xTempStream, embed::ElementModes::READWRITE,
+ uno::Sequence<beans::PropertyValue>(), m_xContext,
+ embed::StorageFormats::PACKAGE));
+}
+
+uno::Reference< uno::XInterface > SAL_CALL OStorageFactory::createInstanceWithArguments(
+ const uno::Sequence< uno::Any >& aArguments )
+{
+ // The request for storage can be done with up to three arguments
+
+ // The first argument specifies a source for the storage
+ // it can be URL, XStream, XInputStream.
+ // The second value is a mode the storage should be open in.
+ // And the third value is a media descriptor.
+
+ sal_Int32 nArgNum = aArguments.getLength();
+ OSL_ENSURE( nArgNum < 4, "Wrong parameter number" );
+
+ if ( !nArgNum )
+ return createInstance();
+
+ // first try to retrieve storage open mode if any
+ // by default the storage will be open in readonly mode
+ sal_Int32 nStorageMode = embed::ElementModes::READ;
+ if ( nArgNum >= 2 )
+ {
+ if( !( aArguments[1] >>= nStorageMode ) )
+ {
+ OSL_FAIL( "Wrong second argument!" );
+ throw lang::IllegalArgumentException(); // TODO:
+ }
+ // it's always possible to read written storage in this implementation
+ nStorageMode |= embed::ElementModes::READ;
+ }
+
+ if ( ( nStorageMode & embed::ElementModes::TRUNCATE ) == embed::ElementModes::TRUNCATE
+ && ( nStorageMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE )
+ throw lang::IllegalArgumentException(); // TODO:
+
+ // retrieve storage source stream
+ OUString aURL;
+ uno::Reference< io::XStream > xStream;
+ uno::Reference< io::XInputStream > xInputStream;
+
+ if ( aArguments[0] >>= aURL )
+ {
+ if ( aURL.isEmpty() )
+ {
+ OSL_FAIL( "Empty URL is provided!" );
+ throw lang::IllegalArgumentException(); // TODO:
+ }
+
+ if ( aURL.startsWithIgnoreAsciiCase("vnd.sun.star.pkg:") )
+ {
+ OSL_FAIL( "Packages URL's are not valid for storages!" ); // ???
+ throw lang::IllegalArgumentException(); // TODO:
+ }
+
+ uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess(
+ ucb::SimpleFileAccess::create(
+ m_xContext ) );
+
+ if ( nStorageMode & embed::ElementModes::WRITE )
+ xStream = xTempAccess->openFileReadWrite( aURL );
+ else
+ xInputStream = xTempAccess->openFileRead( aURL );
+ }
+ else if ( !( aArguments[0] >>= xStream ) && !( aArguments[0] >>= xInputStream ) )
+ {
+ OSL_FAIL( "Wrong first argument!" );
+ throw uno::Exception("wrong first arg", nullptr); // TODO: Illegal argument
+ }
+
+ // retrieve mediadescriptor and set storage properties
+ uno::Sequence< beans::PropertyValue > aDescr;
+ uno::Sequence< beans::PropertyValue > aPropsToSet;
+
+ sal_Int32 nStorageType = embed::StorageFormats::PACKAGE;
+
+ if ( nArgNum >= 3 )
+ {
+ if( aArguments[2] >>= aDescr )
+ {
+ if ( !aURL.isEmpty() )
+ {
+ aPropsToSet = { comphelper::makePropertyValue("URL", aURL) };
+ }
+
+ sal_Int32 nNumArgs = 1;
+ for ( const auto& rProp : std::as_const(aDescr) )
+ {
+ if ( rProp.Name == "InteractionHandler"
+ || rProp.Name == "Password"
+ || rProp.Name == "RepairPackage"
+ || rProp.Name == "StatusIndicator" )
+ {
+ aPropsToSet.realloc( ++nNumArgs );
+ auto pPropsToSet = aPropsToSet.getArray();
+ pPropsToSet[nNumArgs-1].Name = rProp.Name;
+ pPropsToSet[nNumArgs-1].Value = rProp.Value;
+ }
+ else if ( rProp.Name == "StorageFormat" )
+ {
+ OUString aFormatName;
+ sal_Int32 nFormatID = 0;
+ if ( rProp.Value >>= aFormatName )
+ {
+ if ( aFormatName == PACKAGE_STORAGE_FORMAT_STRING )
+ nStorageType = embed::StorageFormats::PACKAGE;
+ else if ( aFormatName == ZIP_STORAGE_FORMAT_STRING )
+ nStorageType = embed::StorageFormats::ZIP;
+ else if ( aFormatName == OFOPXML_STORAGE_FORMAT_STRING )
+ nStorageType = embed::StorageFormats::OFOPXML;
+ else
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+ }
+ else if ( rProp.Value >>= nFormatID )
+ {
+ if ( nFormatID != embed::StorageFormats::PACKAGE
+ && nFormatID != embed::StorageFormats::ZIP
+ && nFormatID != embed::StorageFormats::OFOPXML )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ nStorageType = nFormatID;
+ }
+ else
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+ }
+ else if (rProp.Name == "NoFileSync")
+ {
+ // Forward NoFileSync to the storage.
+ aPropsToSet.realloc(++nNumArgs);
+ auto pPropsToSet = aPropsToSet.getArray();
+ pPropsToSet[nNumArgs - 1].Name = rProp.Name;
+ pPropsToSet[nNumArgs - 1].Value = rProp.Value;
+ }
+ else
+ OSL_FAIL( "Unacceptable property, will be ignored!" );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "Wrong third argument!" );
+ throw uno::Exception("wrong 3rd arg", nullptr); // TODO: Illegal argument
+ }
+
+ }
+
+ // create storage based on source
+ if ( xInputStream.is() )
+ {
+ // if xInputStream is set the storage should be open from it
+ if ( nStorageMode & embed::ElementModes::WRITE )
+ throw uno::Exception("storagemode==write", nullptr); // TODO: access denied
+
+ uno::Reference< io::XSeekable > xSeekable( xInputStream, uno::UNO_QUERY );
+ if ( !xSeekable.is() )
+ {
+ // TODO: wrap stream to let it be seekable
+ OSL_FAIL( "Nonseekable streams are not supported for now!" );
+ }
+
+ if ( !CheckPackageSignature_Impl( xInputStream, xSeekable ) )
+ throw io::IOException("package signature check failed, probably not a package file", nullptr); // TODO: this is not a package file
+
+ return cppu::getXWeak(
+ new OStorage(xInputStream, nStorageMode, aPropsToSet, m_xContext, nStorageType));
+ }
+ else if ( xStream.is() )
+ {
+ if ( ( ( nStorageMode & embed::ElementModes::WRITE ) && !xStream->getOutputStream().is() )
+ || !xStream->getInputStream().is() )
+ throw uno::Exception("access denied", nullptr); // TODO: access denied
+
+ uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
+ if ( !xSeekable.is() )
+ {
+ // TODO: wrap stream to let it be seekable
+ OSL_FAIL( "Nonseekable streams are not supported for now!" );
+ }
+
+ if ( !CheckPackageSignature_Impl( xStream->getInputStream(), xSeekable ) )
+ throw io::IOException(); // TODO: this is not a package file
+
+ return cppu::getXWeak(
+ new OStorage(xStream, nStorageMode, aPropsToSet, m_xContext, nStorageType));
+ }
+
+ throw uno::Exception("no input stream or regular stream", nullptr); // general error during creation
+}
+
+OUString SAL_CALL OStorageFactory::getImplementationName()
+{
+ return "com.sun.star.comp.embed.StorageFactory";
+}
+
+sal_Bool SAL_CALL OStorageFactory::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL OStorageFactory::getSupportedServiceNames()
+{
+ return { "com.sun.star.embed.StorageFactory",
+ "com.sun.star.comp.embed.StorageFactory" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+package_OStorageFactory_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new OStorageFactory(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/xfactory.hxx b/package/source/xstor/xfactory.hxx
new file mode 100644
index 0000000000..3669118492
--- /dev/null
+++ b/package/source/xstor/xfactory.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_XFACTORY_HXX
+#define INCLUDED_PACKAGE_SOURCE_XSTOR_XFACTORY_HXX
+
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <osl/diagnose.h>
+
+class OStorageFactory : public ::cppu::WeakImplHelper< css::lang::XSingleServiceFactory,
+ css::lang::XServiceInfo >
+{
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+public:
+ explicit OStorageFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext )
+ : m_xContext( xContext )
+ {
+ OSL_ENSURE( xContext.is(), "No service manager is provided!" );
+ }
+
+ // XSingleServiceFactory
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance() override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/xstor.component b/package/source/xstor/xstor.component
new file mode 100644
index 0000000000..ff6de82b65
--- /dev/null
+++ b/package/source/xstor/xstor.component
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.embed.StorageFactory"
+ constructor="package_OStorageFactory_get_implementation" single-instance="true">
+ <service name="com.sun.star.comp.embed.StorageFactory"/>
+ <service name="com.sun.star.embed.StorageFactory"/>
+ </implementation>
+</component>
diff --git a/package/source/xstor/xstorage.cxx b/package/source/xstor/xstorage.cxx
new file mode 100644
index 0000000000..d19d1cac7f
--- /dev/null
+++ b/package/source/xstor/xstorage.cxx
@@ -0,0 +1,5493 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <cassert>
+#include <string_view>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/InvalidStorageException.hpp>
+#include <com/sun/star/embed/UseBackupException.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
+#include <com/sun/star/packages/NoEncryptionException.hpp>
+#include <com/sun/star/packages/NoRawFormatException.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+
+#include <PackageConstants.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
+#include <utility>
+#include <comphelper/diagnose_ex.hxx>
+
+#include "xstorage.hxx"
+#include "owriteablestream.hxx"
+#include "switchpersistencestream.hxx"
+
+using namespace ::com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+// static
+void OStorage_Impl::completeStorageStreamCopy_Impl(
+ const uno::Reference< io::XStream >& xSource,
+ const uno::Reference< io::XStream >& xDest,
+ sal_Int32 nStorageType,
+ const uno::Sequence< uno::Sequence< beans::StringPair > >& aRelInfo )
+{
+ uno::Reference< beans::XPropertySet > xSourceProps( xSource, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xDestProps( xDest, uno::UNO_QUERY_THROW );
+
+ uno::Reference< io::XOutputStream > xDestOutStream = xDest->getOutputStream();
+ if ( !xDestOutStream.is() )
+ throw io::IOException( THROW_WHERE );
+
+ uno::Reference< io::XInputStream > xSourceInStream = xSource->getInputStream();
+ if ( !xSourceInStream.is() )
+ throw io::IOException( THROW_WHERE );
+
+ // TODO: headers of encrypted streams should be copied also
+ ::comphelper::OStorageHelper::CopyInputToOutput( xSourceInStream, xDestOutStream );
+
+ uno::Sequence<OUString> aPropNames { "Compressed", "MediaType",
+ "UseCommonStoragePasswordEncryption" };
+
+ if ( nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ // TODO/LATER: in future it might make sense to provide the stream if there is one
+ uno::Reference< embed::XRelationshipAccess > xRelAccess( xDest, uno::UNO_QUERY_THROW );
+ xRelAccess->clearRelationships();
+ xRelAccess->insertRelationships( aRelInfo, false );
+
+ aPropNames.realloc( 2 );
+ }
+ else if ( nStorageType != embed::StorageFormats::PACKAGE )
+ {
+ aPropNames.realloc( 1 );
+ }
+
+ for ( const auto& rPropName : std::as_const(aPropNames) )
+ xDestProps->setPropertyValue( rPropName, xSourceProps->getPropertyValue( rPropName ) );
+}
+
+static uno::Reference< io::XInputStream > GetSeekableTempCopy( const uno::Reference< io::XInputStream >& xInStream )
+{
+ rtl::Reference < utl::TempFileFastService > xTempFile = new utl::TempFileFastService;
+ uno::Reference < io::XOutputStream > xTempOut = xTempFile->getOutputStream();
+ uno::Reference < io::XInputStream > xTempIn = xTempFile->getInputStream();
+
+ if ( !xTempOut.is() || !xTempIn.is() )
+ throw io::IOException( THROW_WHERE );
+
+ ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOut );
+ xTempOut->closeOutput();
+
+ return xTempIn;
+}
+
+SotElement_Impl::SotElement_Impl(OUString aName, bool bStor, bool bNew)
+ : m_aOriginalName(std::move(aName))
+ , m_bIsRemoved(false)
+ , m_bIsInserted(bNew)
+ , m_bIsStorage(bStor)
+{
+}
+
+// most of properties are holt by the storage but are not used
+OStorage_Impl::OStorage_Impl( uno::Reference< io::XInputStream > const & xInputStream,
+ sal_Int32 nMode,
+ const uno::Sequence< beans::PropertyValue >& xProperties,
+ uno::Reference< uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType )
+: m_xMutex( new comphelper::RefCountedMutex )
+, m_pAntiImpl( nullptr )
+, m_nStorageMode( nMode & ~embed::ElementModes::SEEKABLE )
+, m_bIsModified( ( nMode & ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) == ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) )
+, m_bBroadcastModified( false )
+, m_bCommited( false )
+, m_bIsRoot( true )
+, m_bListCreated( false )
+, m_nModifiedListenerCount( 0 )
+, m_xContext( xContext )
+, m_xProperties( xProperties )
+, m_bHasCommonEncryptionData( false )
+, m_pParent( nullptr )
+, m_bControlMediaType( false )
+, m_bMTFallbackUsed( false )
+, m_bControlVersion( false )
+, m_nStorageType( nStorageType )
+, m_pRelStorElement( nullptr )
+, m_nRelInfoStatus( RELINFO_NO_INIT )
+{
+ // all the checks done below by assertion statements must be done by factory
+ SAL_WARN_IF( !xInputStream.is(), "package.xstor", "No input stream is provided!" );
+ assert(xContext.is());
+
+ m_pSwitchStream = new SwitchablePersistenceStream(xInputStream);
+ m_xInputStream = m_pSwitchStream->getInputStream();
+
+ if ( m_nStorageMode & embed::ElementModes::WRITE )
+ {
+ // check that the stream allows to write
+ SAL_WARN( "package.xstor", "No stream for writing is provided!" );
+ }
+}
+
+// most of properties are holt by the storage but are not used
+OStorage_Impl::OStorage_Impl( uno::Reference< io::XStream > const & xStream,
+ sal_Int32 nMode,
+ const uno::Sequence< beans::PropertyValue >& xProperties,
+ uno::Reference< uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType )
+: m_xMutex( new comphelper::RefCountedMutex )
+, m_pAntiImpl( nullptr )
+, m_nStorageMode( nMode & ~embed::ElementModes::SEEKABLE )
+, m_bIsModified( ( nMode & ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) == ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) )
+, m_bBroadcastModified( false )
+, m_bCommited( false )
+, m_bIsRoot( true )
+, m_bListCreated( false )
+, m_nModifiedListenerCount( 0 )
+, m_xContext( xContext )
+, m_xProperties( xProperties )
+, m_bHasCommonEncryptionData( false )
+, m_pParent( nullptr )
+, m_bControlMediaType( false )
+, m_bMTFallbackUsed( false )
+, m_bControlVersion( false )
+, m_nStorageType( nStorageType )
+, m_pRelStorElement( nullptr )
+, m_nRelInfoStatus( RELINFO_NO_INIT )
+{
+ // all the checks done below by assertion statements must be done by factory
+ SAL_WARN_IF( !xStream.is(), "package.xstor", "No stream is provided!" );
+ assert(xContext.is());
+
+ if ( m_nStorageMode & embed::ElementModes::WRITE )
+ {
+ m_pSwitchStream = new SwitchablePersistenceStream(xStream);
+ m_xStream = static_cast< io::XStream* >( m_pSwitchStream.get() );
+ }
+ else
+ {
+ m_pSwitchStream = new SwitchablePersistenceStream(xStream->getInputStream());
+ m_xInputStream = m_pSwitchStream->getInputStream();
+ }
+}
+
+OStorage_Impl::OStorage_Impl( OStorage_Impl* pParent,
+ sal_Int32 nMode,
+ uno::Reference< container::XNameContainer > const & xPackageFolder,
+ uno::Reference< lang::XSingleServiceFactory > xPackage,
+ uno::Reference< uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType )
+: m_xMutex( new comphelper::RefCountedMutex )
+, m_pAntiImpl( nullptr )
+, m_nStorageMode( nMode & ~embed::ElementModes::SEEKABLE )
+, m_bIsModified( ( nMode & ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) == ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) )
+, m_bBroadcastModified( false )
+, m_bCommited( false )
+, m_bIsRoot( false )
+, m_bListCreated( false )
+, m_nModifiedListenerCount( 0 )
+, m_xPackageFolder( xPackageFolder )
+, m_xPackage(std::move( xPackage ))
+, m_xContext( xContext )
+, m_bHasCommonEncryptionData( false )
+, m_pParent( pParent ) // can be empty in case of temporary readonly substorages and relation storage
+, m_bControlMediaType( false )
+, m_bMTFallbackUsed( false )
+, m_bControlVersion( false )
+, m_nStorageType( nStorageType )
+, m_pRelStorElement( nullptr )
+, m_nRelInfoStatus( RELINFO_NO_INIT )
+{
+ SAL_WARN_IF( !xPackageFolder.is(), "package.xstor", "No package folder!" );
+ assert(xContext.is());
+}
+
+OStorage_Impl::~OStorage_Impl()
+{
+ {
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+ if ( m_pAntiImpl ) // root storage wrapper must set this member to NULL before destruction of object
+ {
+ SAL_WARN_IF( m_bIsRoot, "package.xstor", "The root storage wrapper must be disposed already" );
+
+ try {
+ m_pAntiImpl->InternalDispose( false );
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+ }
+ m_pAntiImpl = nullptr;
+ }
+ else if ( !m_aReadOnlyWrapVector.empty() )
+ {
+ for ( auto& rStorage : m_aReadOnlyWrapVector )
+ {
+ uno::Reference< embed::XStorage > xTmp = rStorage.m_xWeakRef;
+ if ( xTmp.is() )
+ try {
+ rStorage.m_pPointer->InternalDispose( false );
+ } catch( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+ }
+ }
+
+ m_aReadOnlyWrapVector.clear();
+ }
+
+ m_pParent = nullptr;
+ }
+
+ for (const auto & pair : m_aChildrenMap)
+ for (auto pElement : pair.second)
+ delete pElement;
+ m_aChildrenMap.clear();
+
+ std::for_each(m_aDeletedVector.begin(), m_aDeletedVector.end(), std::default_delete<SotElement_Impl>());
+ m_aDeletedVector.clear();
+
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML && m_pRelStorElement )
+ {
+ delete m_pRelStorElement;
+ m_pRelStorElement = nullptr;
+ }
+
+ m_xPackageFolder.clear();
+ m_xPackage.clear();
+
+ OUString aPropertyName = "URL";
+ for ( const auto& rProp : std::as_const(m_xProperties) )
+ {
+ if ( rProp.Name == aPropertyName )
+ {
+ // the storage is URL based so all the streams are opened by factory and should be closed
+ try
+ {
+ if ( m_xInputStream.is() )
+ {
+ m_xInputStream->closeInput();
+ m_xInputStream.clear();
+ }
+
+ if ( m_xStream.is() )
+ {
+ uno::Reference< io::XInputStream > xInStr = m_xStream->getInputStream();
+ if ( xInStr.is() )
+ xInStr->closeInput();
+
+ uno::Reference< io::XOutputStream > xOutStr = m_xStream->getOutputStream();
+ if ( xOutStr.is() )
+ xOutStr->closeOutput();
+
+ m_xStream.clear();
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+ }
+ }
+ }
+}
+
+void OStorage_Impl::SetReadOnlyWrap( OStorage& aStorage )
+{
+ // Weak reference is used inside the holder so the refcount must not be zero at this point
+ OSL_ENSURE( aStorage.GetRefCount_Impl(), "There must be a reference alive to use this method!" );
+ m_aReadOnlyWrapVector.emplace_back( &aStorage );
+}
+
+void OStorage_Impl::RemoveReadOnlyWrap( const OStorage& aStorage )
+{
+ for ( StorageHoldersType::iterator pStorageIter = m_aReadOnlyWrapVector.begin();
+ pStorageIter != m_aReadOnlyWrapVector.end();)
+ {
+ uno::Reference< embed::XStorage > xTmp = pStorageIter->m_xWeakRef;
+ if ( !xTmp.is() || pStorageIter->m_pPointer == &aStorage )
+ {
+ try {
+ pStorageIter->m_pPointer->InternalDispose( false );
+ } catch( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+ }
+
+ pStorageIter = m_aReadOnlyWrapVector.erase(pStorageIter);
+ }
+ else
+ ++pStorageIter;
+ }
+}
+
+void OStorage_Impl::OpenOwnPackage()
+{
+ SAL_WARN_IF( !m_bIsRoot, "package.xstor", "Opening of the package has no sense!" );
+
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xPackageFolder.is() )
+ {
+ if ( !m_xPackage.is() )
+ {
+ uno::Sequence< uno::Any > aArguments( 2 );
+ auto pArguments = aArguments.getArray();
+ if ( m_nStorageMode & embed::ElementModes::WRITE )
+ pArguments[ 0 ] <<= m_xStream;
+ else
+ {
+ SAL_WARN_IF( !m_xInputStream.is(), "package.xstor", "Input stream must be set for readonly access!" );
+ pArguments[ 0 ] <<= m_xInputStream;
+ // TODO: if input stream is not seekable or XSeekable interface is supported
+ // on XStream object a wrapper must be used
+ }
+
+ // do not allow elements to remove themself from the old container in case of insertion to another container
+ pArguments[ 1 ] <<= beans::NamedValue( "AllowRemoveOnInsert",
+ uno::Any( false ) );
+
+ sal_Int32 nArgNum = 2;
+ for ( const auto& rProp : std::as_const(m_xProperties) )
+ {
+ if ( rProp.Name == "RepairPackage"
+ || rProp.Name == "ProgressHandler"
+ || rProp.Name == "NoFileSync" )
+ {
+ // Forward these to the package.
+ beans::NamedValue aNamedValue( rProp.Name, rProp.Value );
+ aArguments.realloc( ++nArgNum );
+ pArguments = aArguments.getArray();
+ pArguments[nArgNum-1] <<= aNamedValue;
+ if (rProp.Name == "RepairPackage")
+ rProp.Value >>= m_bRepairPackage;
+ }
+ else if ( rProp.Name == "Password" )
+ {
+ // TODO: implement password setting for documents
+ // the password entry must be removed after setting
+ }
+ }
+
+ if ( m_nStorageType == embed::StorageFormats::ZIP )
+ {
+ // let the package support only plain zip format
+ beans::NamedValue aNamedValue;
+ aNamedValue.Name = "StorageFormat";
+ aNamedValue.Value <<= OUString( "ZipFormat" );
+ aArguments.realloc( ++nArgNum );
+ pArguments = aArguments.getArray();
+ pArguments[nArgNum-1] <<= aNamedValue;
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ // let the package support OFOPXML media type handling
+ beans::NamedValue aNamedValue;
+ aNamedValue.Name = "StorageFormat";
+ aNamedValue.Value <<= OUString( "OFOPXMLFormat" );
+ aArguments.realloc( ++nArgNum );
+ pArguments = aArguments.getArray();
+ pArguments[nArgNum-1] <<= aNamedValue;
+ }
+
+ m_xPackage.set( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.packages.comp.ZipPackage", aArguments, m_xContext),
+ uno::UNO_QUERY );
+ }
+
+ uno::Reference< container::XHierarchicalNameAccess > xHNameAccess( m_xPackage, uno::UNO_QUERY );
+ SAL_WARN_IF( !xHNameAccess.is(), "package.xstor", "The package could not be created!" );
+
+ if ( xHNameAccess.is() )
+ {
+ uno::Any aFolder = xHNameAccess->getByHierarchicalName("/");
+ aFolder >>= m_xPackageFolder;
+ }
+ }
+
+ SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor", "The package root folder can not be opened!" );
+ if ( !m_xPackageFolder.is() )
+ throw embed::InvalidStorageException( THROW_WHERE );
+}
+
+bool OStorage_Impl::HasChildren()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ ReadContents();
+ return !m_aChildrenMap.empty();
+}
+
+void OStorage_Impl::GetStorageProperties()
+{
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ return;
+
+ uno::Reference< beans::XPropertySet > xProps( m_xPackageFolder, uno::UNO_QUERY_THROW );
+
+ if ( !m_bControlMediaType )
+ {
+ uno::Reference< beans::XPropertySet > xPackageProps( m_xPackage, uno::UNO_QUERY_THROW );
+ xPackageProps->getPropertyValue( MEDIATYPE_FALLBACK_USED_PROPERTY ) >>= m_bMTFallbackUsed;
+
+ static constexpr OUStringLiteral sMediaType(u"MediaType");
+ xProps->getPropertyValue( sMediaType ) >>= m_aMediaType;
+ m_bControlMediaType = true;
+ }
+
+ if ( !m_bControlVersion )
+ {
+ xProps->getPropertyValue( "Version" ) >>= m_aVersion;
+ m_bControlVersion = true;
+ }
+
+ // the properties of OFOPXML will be handled directly
+}
+
+void OStorage_Impl::ReadRelInfoIfNecessary()
+{
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ return;
+
+ if ( m_nRelInfoStatus == RELINFO_NO_INIT )
+ {
+ // Init from original stream
+ uno::Reference< io::XInputStream > xRelInfoStream
+ = GetRelInfoStreamForName( std::u16string_view() );
+ try
+ {
+ if ( xRelInfoStream.is() )
+ m_aRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(
+ xRelInfoStream,
+ u"_rels/.rels",
+ m_xContext );
+ m_nRelInfoStatus = RELINFO_READ;
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "");
+ }
+ }
+ else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
+ {
+ // Init from the new stream
+ try
+ {
+ if ( m_xNewRelInfoStream.is() )
+ m_aRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(
+ m_xNewRelInfoStream,
+ u"_rels/.rels",
+ m_xContext );
+
+ m_nRelInfoStatus = RELINFO_CHANGED_STREAM_READ;
+ }
+ catch( const uno::Exception& )
+ {
+ m_nRelInfoStatus = RELINFO_CHANGED_BROKEN;
+ }
+ }
+}
+
+void OStorage_Impl::ReadContents()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( m_bListCreated )
+ return;
+
+ if ( m_bIsRoot )
+ OpenOwnPackage();
+
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xPackageFolder, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
+ if ( !xEnum.is() )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ m_bListCreated = true;
+
+ while( xEnum->hasMoreElements() )
+ {
+ try {
+ uno::Reference< container::XNamed > xNamed;
+ xEnum->nextElement() >>= xNamed;
+
+ if ( !xNamed.is() )
+ {
+ SAL_WARN( "package.xstor", "XNamed is not supported!" );
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+
+ OUString aName = xNamed->getName();
+ SAL_WARN_IF( aName.isEmpty(), "package.xstor", "Empty name!" );
+
+ uno::Reference< container::XNameContainer > xNameContainer( xNamed, uno::UNO_QUERY );
+
+ std::unique_ptr<SotElement_Impl> xNewElement(new SotElement_Impl(aName, xNameContainer.is(), false));
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML && aName == "_rels" )
+ {
+ if (!xNewElement->m_bIsStorage)
+ throw io::IOException( THROW_WHERE ); // TODO: Unexpected format
+
+ m_pRelStorElement = xNewElement.release();
+ CreateRelStorage();
+ }
+ else
+ {
+ if ( ( m_nStorageMode & embed::ElementModes::TRUNCATE ) == embed::ElementModes::TRUNCATE )
+ {
+ // if a storage is truncated all of it elements are marked as deleted
+ xNewElement->m_bIsRemoved = true;
+ }
+
+ m_aChildrenMap[aName].push_back(xNewElement.release());
+ }
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_WARN_EXCEPTION( "package.xstor", "hasMoreElements() implementation has problems!");
+ break;
+ }
+ }
+ if ( ( m_nStorageMode & embed::ElementModes::TRUNCATE ) == embed::ElementModes::TRUNCATE )
+ {
+ // if a storage is truncated the relations information should be cleaned
+ m_xNewRelInfoStream.clear();
+ m_aRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
+ m_nRelInfoStatus = RELINFO_CHANGED;
+ }
+
+ // cache changeable folder properties
+ GetStorageProperties();
+}
+
+void OStorage_Impl::CopyToStorage( const uno::Reference< embed::XStorage >& xDest, bool bDirect )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ uno::Reference< beans::XPropertySet > xPropSet( xDest, uno::UNO_QUERY );
+ if ( !xPropSet.is() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ sal_Int32 nDestMode = embed::ElementModes::READ;
+ xPropSet->getPropertyValue( "OpenMode" ) >>= nDestMode;
+
+ if ( !( nDestMode & embed::ElementModes::WRITE ) )
+ throw io::IOException( THROW_WHERE ); // TODO: access_denied
+
+ ReadContents();
+
+ if ( !m_xPackageFolder.is() )
+ throw embed::InvalidStorageException( THROW_WHERE );
+
+ for ( const auto& pair : m_aChildrenMap )
+ for (auto pElement : pair.second)
+ {
+ if ( !pElement->m_bIsRemoved )
+ CopyStorageElement( pElement, xDest, /*aName*/pair.first, bDirect );
+ }
+
+ // move storage properties to the destination one ( means changeable properties )
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ xPropSet->setPropertyValue( "MediaType", uno::Any( m_aMediaType ) );
+ xPropSet->setPropertyValue( "Version", uno::Any( m_aVersion ) );
+ }
+
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ // if this is a root storage, the common key from current one should be moved there
+ bool bIsRoot = false;
+ if ( ( xPropSet->getPropertyValue( "IsRoot" ) >>= bIsRoot ) && bIsRoot )
+ {
+ try
+ {
+ uno::Reference< embed::XEncryptionProtectedStorage > xEncr( xDest, uno::UNO_QUERY );
+ if ( xEncr.is() )
+ {
+ xEncr->setEncryptionData( GetCommonRootEncryptionData().getAsConstNamedValueList() );
+
+ uno::Sequence< beans::NamedValue > aAlgorithms;
+ uno::Reference< beans::XPropertySet > xPackPropSet( m_xPackage, uno::UNO_QUERY_THROW );
+ xPackPropSet->getPropertyValue( ENCRYPTION_ALGORITHMS_PROPERTY )
+ >>= aAlgorithms;
+ xEncr->setEncryptionAlgorithms( aAlgorithms );
+ }
+ }
+ catch( const packages::NoEncryptionException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "No Encryption");
+ }
+ }
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+
+ // TODO/LATER: currently the optimization is not active
+ // uno::Reference< io::XInputStream > xRelInfoStream = GetRelInfoStreamForName( OUString() ); // own stream
+ // if ( xRelInfoStream.is() )
+ // {
+ // // Relations info stream is a writeonly property, introduced only to optimize copying
+ // // Should be used carefully since no check for stream consistency is done, and the stream must not stay locked
+
+ // OUString aRelInfoString = "RelationsInfoStream";
+ // xPropSet->setPropertyValue( aRelInfoString, uno::makeAny( GetSeekableTempCopy( xRelInfoStream, m_xFactory ) ) );
+ // }
+
+ uno::Reference< embed::XRelationshipAccess > xRels( xDest, uno::UNO_QUERY );
+ if ( !xRels.is() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ xRels->insertRelationships( GetAllRelationshipsIfAny(), false );
+ }
+
+ // if possible the destination storage should be committed after successful copying
+ uno::Reference< embed::XTransactedObject > xObjToCommit( xDest, uno::UNO_QUERY );
+ if ( xObjToCommit.is() )
+ xObjToCommit->commit();
+}
+
+void OStorage_Impl::CopyStorageElement( SotElement_Impl* pElement,
+ const uno::Reference< embed::XStorage >& xDest,
+ const OUString& aName,
+ bool bDirect )
+{
+ SAL_WARN_IF( !xDest.is(), "package.xstor", "No destination storage!" );
+ SAL_WARN_IF( aName.isEmpty(), "package.xstor", "Empty element name!" );
+
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ uno::Reference< container::XNameAccess > xDestAccess( xDest, uno::UNO_QUERY_THROW );
+ if ( xDestAccess->hasByName( aName )
+ && !( pElement->m_bIsStorage && xDest->isStorageElement( aName ) ) )
+ xDest->removeElement( aName );
+
+ if ( pElement->m_bIsStorage )
+ {
+ uno::Reference< embed::XStorage > xSubDest =
+ xDest->openStorageElement( aName,
+ embed::ElementModes::WRITE );
+
+ SAL_WARN_IF( !xSubDest.is(), "package.xstor", "No destination substorage!" );
+
+ if (!pElement->m_xStorage)
+ {
+ OpenSubStorage( pElement, embed::ElementModes::READ );
+ if (!pElement->m_xStorage)
+ throw io::IOException( THROW_WHERE );
+ }
+
+ pElement->m_xStorage->CopyToStorage(xSubDest, bDirect);
+ }
+ else
+ {
+ if (!pElement->m_xStream)
+ {
+ OpenSubStream( pElement );
+ if (!pElement->m_xStream)
+ throw io::IOException( THROW_WHERE );
+ }
+
+ if (!pElement->m_xStream->IsEncrypted())
+ {
+ if ( bDirect )
+ {
+ // fill in the properties for the stream
+ uno::Sequence< beans::PropertyValue > aStrProps(0);
+ const uno::Sequence< beans::PropertyValue > aSrcPkgProps = pElement->m_xStream->GetStreamProperties();
+ sal_Int32 nNum = 0;
+ for ( const auto& rSrcPkgProp : aSrcPkgProps )
+ {
+ if ( rSrcPkgProp.Name == "MediaType" || rSrcPkgProp.Name == "Compressed" )
+ {
+ aStrProps.realloc( ++nNum );
+ auto pStrProps = aStrProps.getArray();
+ pStrProps[nNum-1].Name = rSrcPkgProp.Name;
+ pStrProps[nNum-1].Value = rSrcPkgProp.Value;
+ }
+ }
+
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ aStrProps.realloc( ++nNum );
+ auto pStrProps = aStrProps.getArray();
+ pStrProps[nNum-1].Name = "UseCommonStoragePasswordEncryption";
+ pStrProps[nNum-1].Value <<= pElement->m_xStream->UsesCommonEncryption_Impl();
+ }
+ else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ // TODO/LATER: currently the optimization is not active
+ // uno::Reference< io::XInputStream > xInStream = GetRelInfoStreamForName( OUString() ); // own rels stream
+ // if ( xInStream.is() )
+ // {
+ // aStrProps.realloc( ++nNum );
+ // aStrProps[nNum-1].Name = "RelationsInfoStream";
+ // aStrProps[nNum-1].Value <<= GetSeekableTempCopy( xInStream, m_xFactory );
+ // }
+
+ uno::Reference< embed::XRelationshipAccess > xRels( xDest, uno::UNO_QUERY );
+ if ( !xRels.is() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ xRels->insertRelationships( GetAllRelationshipsIfAny(), false );
+ }
+
+ uno::Reference< embed::XOptimizedStorage > xOptDest( xDest, uno::UNO_QUERY_THROW );
+ uno::Reference < io::XInputStream > xInputToInsert;
+
+ if (pElement->m_xStream->HasTempFile_Impl() || !pElement->m_xStream->m_xPackageStream.is())
+ {
+ SAL_WARN_IF(!pElement->m_xStream->m_xPackageStream.is(), "package.xstor", "No package stream!");
+
+ // if the stream is modified - the temporary file must be used for insertion
+ xInputToInsert = pElement->m_xStream->GetTempFileAsInputStream();
+ }
+ else
+ {
+ // for now get just nonseekable access to the stream
+ // TODO/LATER: the raw stream can be used
+
+ xInputToInsert = pElement->m_xStream->m_xPackageStream->getDataStream();
+ }
+
+ if ( !xInputToInsert.is() )
+ throw io::IOException( THROW_WHERE );
+
+ xOptDest->insertStreamElementDirect( aName, xInputToInsert, aStrProps );
+ }
+ else
+ {
+ uno::Reference< io::XStream > xSubStr =
+ xDest->openStreamElement( aName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ SAL_WARN_IF( !xSubStr.is(), "package.xstor", "No destination substream!" );
+
+ pElement->m_xStream->CopyInternallyTo_Impl(xSubStr);
+ }
+ }
+ else if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ {
+ SAL_WARN( "package.xstor", "Encryption is only supported in package storage!" );
+ throw io::IOException( THROW_WHERE );
+ }
+ else if ( pElement->m_xStream->HasCachedEncryptionData()
+ && ( pElement->m_xStream->IsModified() || pElement->m_xStream->HasWriteOwner_Impl() ) )
+ {
+ ::comphelper::SequenceAsHashMap aCommonEncryptionData;
+ bool bHasCommonEncryptionData = false;
+ try
+ {
+ aCommonEncryptionData = GetCommonRootEncryptionData();
+ bHasCommonEncryptionData = true;
+ }
+ catch( const packages::NoEncryptionException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "No Encryption");
+ }
+
+ if (bHasCommonEncryptionData && ::package::PackageEncryptionDataLessOrEqual(pElement->m_xStream->GetCachedEncryptionData(), aCommonEncryptionData))
+ {
+ // If the stream can be opened with the common storage password
+ // it must be stored with the common storage password as well
+ uno::Reference< io::XStream > xDestStream =
+ xDest->openStreamElement( aName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+
+ pElement->m_xStream->CopyInternallyTo_Impl( xDestStream );
+
+ uno::Reference< beans::XPropertySet > xProps( xDestStream, uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue(
+ "UseCommonStoragePasswordEncryption",
+ uno::Any( true ) );
+ }
+ else
+ {
+ // the stream is already opened for writing or was changed
+ uno::Reference< embed::XStorage2 > xDest2( xDest, uno::UNO_QUERY_THROW );
+ uno::Reference< io::XStream > xSubStr =
+ xDest2->openEncryptedStream( aName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE,
+ pElement->m_xStream->GetCachedEncryptionData().getAsConstNamedValueList() );
+ SAL_WARN_IF( !xSubStr.is(), "package.xstor", "No destination substream!" );
+
+ pElement->m_xStream->CopyInternallyTo_Impl(xSubStr, pElement->m_xStream->GetCachedEncryptionData());
+ }
+ }
+ else
+ {
+ // the stream is not opened at all, so it can be just opened for reading
+ try
+ {
+ // If the stream can be opened with the common storage password
+ // it must be stored with the common storage password as well
+
+ uno::Reference< io::XStream > xOwnStream = pElement->m_xStream->GetStream(embed::ElementModes::READ,
+ false);
+ uno::Reference< io::XStream > xDestStream =
+ xDest->openStreamElement( aName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ SAL_WARN_IF( !xDestStream.is(), "package.xstor", "No destination substream!" );
+ completeStorageStreamCopy_Impl( xOwnStream, xDestStream, m_nStorageType, GetAllRelationshipsIfAny() );
+
+ uno::Reference< beans::XPropertySet > xProps( xDestStream, uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue(
+ "UseCommonStoragePasswordEncryption",
+ uno::Any( true ) );
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Handled exception");
+
+ // If the common storage password does not allow to open the stream
+ // it could be copied in raw way, the problem is that the StartKey should be the same
+ // in the ODF1.2 package, so an invalid package could be produced if the stream
+ // is copied from ODF1.1 package, where it is allowed to have different StartKeys
+ uno::Reference< embed::XStorageRawAccess > xRawDest( xDest, uno::UNO_QUERY_THROW );
+ uno::Reference< io::XInputStream > xRawInStream = pElement->m_xStream->GetRawInStream();
+ xRawDest->insertRawEncrStreamElement( aName, xRawInStream );
+ }
+ }
+ }
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > OStorage_Impl::GetAllRelationshipsIfAny()
+{
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ return uno::Sequence< uno::Sequence< beans::StringPair > >();
+
+ ReadRelInfoIfNecessary();
+
+ if ( m_nRelInfoStatus != RELINFO_READ
+ && m_nRelInfoStatus != RELINFO_CHANGED_STREAM_READ
+ && m_nRelInfoStatus != RELINFO_CHANGED )
+ throw io::IOException( THROW_WHERE "Wrong relinfo stream!" );
+ // m_nRelInfoStatus == RELINFO_CHANGED_BROKEN || m_nRelInfoStatus == RELINFO_BROKEN
+ return m_aRelInfo;
+}
+
+void OStorage_Impl::CopyLastCommitTo( const uno::Reference< embed::XStorage >& xNewStor )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor", "A committed storage is incomplete!" );
+ if ( !m_xPackageFolder.is() )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ OStorage_Impl aTempRepresent( nullptr,
+ embed::ElementModes::READ,
+ m_xPackageFolder,
+ m_xPackage,
+ m_xContext,
+ m_nStorageType);
+
+ // TODO/LATER: could use direct copying
+ aTempRepresent.CopyToStorage( xNewStor, false );
+}
+
+void OStorage_Impl::InsertIntoPackageFolder( const OUString& aName,
+ const uno::Reference< container::XNameContainer >& xParentPackageFolder )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor", "An inserted storage is incomplete!" );
+ uno::Reference< uno::XInterface > xTmp( m_xPackageFolder, uno::UNO_QUERY_THROW );
+ xParentPackageFolder->insertByName( aName, uno::Any( xTmp ) );
+
+ m_bCommited = false;
+}
+
+void OStorage_Impl::Commit()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_bIsModified )
+ return;
+
+ // in case of a new empty storage it is possible that the contents are still not read
+ // ( the storage of course has no contents, but the initialization is postponed till the first use,
+ // thus if a new storage was created and committed immediately it must be initialized here )
+ ReadContents();
+
+ // if storage is committed it should have a valid Package representation
+ SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor", "The package representation should exist!" );
+ if ( !m_xPackageFolder.is() )
+ throw embed::InvalidStorageException( THROW_WHERE );
+
+ OSL_ENSURE( m_nStorageMode & embed::ElementModes::WRITE,
+ "Commit of readonly storage, should be detected before!" );
+
+ uno::Reference< container::XNameContainer > xNewPackageFolder;
+
+ // here the storage will switch to the temporary package folder
+ // if the storage was already committed and the parent was not committed after that
+ // the switch should not be done since the package folder in use is a temporary one;
+ // it can be detected by m_bCommited flag ( root storage doesn't need temporary representation )
+ if ( !m_bCommited && !m_bIsRoot )
+ {
+ uno::Sequence< uno::Any > aSeq{ uno::Any(true) };
+ xNewPackageFolder.set( m_xPackage->createInstanceWithArguments( aSeq ),
+ uno::UNO_QUERY );
+ }
+ else
+ xNewPackageFolder = m_xPackageFolder;
+
+ // remove replaced removed elements
+ for ( auto& pDeleted : m_aDeletedVector )
+ {
+
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pDeleted->m_bIsStorage )
+ RemoveStreamRelInfo( pDeleted->m_aOriginalName );
+
+ // the removed elements are not in new temporary storage
+ if ( m_bCommited || m_bIsRoot )
+ xNewPackageFolder->removeByName( pDeleted->m_aOriginalName );
+ delete pDeleted;
+ pDeleted = nullptr;
+ }
+ m_aDeletedVector.clear();
+
+ // remove removed elements
+ for (auto mapIt = m_aChildrenMap.begin(); mapIt != m_aChildrenMap.end(); )
+ {
+ for (auto it = mapIt->second.begin(); it != mapIt->second.end(); )
+ {
+ // renamed and inserted elements must be really inserted to package later
+ // since they can conflict with removed elements
+ auto & pElement = *it;
+ if ( pElement->m_bIsRemoved )
+ {
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pElement->m_bIsStorage )
+ RemoveStreamRelInfo( pElement->m_aOriginalName );
+
+ // the removed elements are not in new temporary storage
+ if ( m_bCommited || m_bIsRoot )
+ xNewPackageFolder->removeByName( pElement->m_aOriginalName );
+
+ delete pElement;
+ it = mapIt->second.erase(it);
+ }
+ else
+ ++it;
+ }
+ if (mapIt->second.empty())
+ mapIt = m_aChildrenMap.erase(mapIt);
+ else
+ ++mapIt;
+ }
+
+
+ // there should be no more deleted elements
+ for ( const auto& pair : m_aChildrenMap )
+ for (auto pElement : pair.second)
+ {
+ // if it is a 'duplicate commit' inserted elements must be really inserted to package later
+ // since they can conflict with renamed elements
+ if ( !pElement->m_bIsInserted )
+ {
+ // for now stream is opened in direct mode that means that in case
+ // storage is committed all the streams from it are committed in current state.
+ // following two steps are separated to allow easily implement transacted mode
+ // for streams if we need it in future.
+ // Only hierarchical access uses transacted streams currently
+ if ( !pElement->m_bIsStorage && pElement->m_xStream
+ && !pElement->m_xStream->IsTransacted() )
+ pElement->m_xStream->Commit();
+
+ // if the storage was not open, there is no need to commit it ???
+ // the storage should be checked that it is committed
+ if (pElement->m_bIsStorage && pElement->m_xStorage && pElement->m_xStorage->m_bCommited)
+ {
+ // it's temporary PackageFolder should be inserted instead of current one
+ // also the new copy of PackageFolder should be used by the children storages
+
+ // the renamed elements are not in new temporary storage
+ if ( m_bCommited || m_bIsRoot )
+ xNewPackageFolder->removeByName( pElement->m_aOriginalName );
+
+ pElement->m_xStorage->InsertIntoPackageFolder(/*aName*/pair.first, xNewPackageFolder);
+ }
+ else if (!pElement->m_bIsStorage && pElement->m_xStream && pElement->m_xStream->m_bFlushed)
+ {
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ CommitStreamRelInfo( /*aName*/pair.first, pElement );
+
+ // the renamed elements are not in new temporary storage
+ if ( m_bCommited || m_bIsRoot )
+ xNewPackageFolder->removeByName( pElement->m_aOriginalName );
+
+ pElement->m_xStream->InsertIntoPackageFolder(/*aName*/pair.first, xNewPackageFolder);
+ }
+ else if ( !m_bCommited && !m_bIsRoot )
+ {
+ // the element must be just copied to the new temporary package folder
+ // the connection with the original package should not be lost just because
+ // the element is still referred by the folder in the original hierarchy
+ uno::Any aPackageElement = m_xPackageFolder->getByName( pElement->m_aOriginalName );
+ xNewPackageFolder->insertByName( /*aName*/pair.first, aPackageElement );
+ }
+ else if ( pair.first != pElement->m_aOriginalName )
+ {
+ // this is the case when xNewPackageFolder refers to m_xPackageFolder
+ // in case the name was changed and it is not a changed storage - rename the element
+ uno::Any aPackageElement = xNewPackageFolder->getByName( pElement->m_aOriginalName );
+ xNewPackageFolder->removeByName( pElement->m_aOriginalName );
+ xNewPackageFolder->insertByName( /*aName*/pair.first, aPackageElement );
+
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pElement->m_bIsStorage )
+ {
+ if (!pElement->m_xStream)
+ {
+ OpenSubStream( pElement );
+ if (!pElement->m_xStream)
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+
+ CommitStreamRelInfo( /*aName*/pair.first, pElement );
+ }
+ }
+
+ pElement->m_aOriginalName = pair.first;
+ }
+ }
+
+ for ( const auto& pair : m_aChildrenMap )
+ for (auto pElement : pair.second)
+ {
+ // now inserted elements can be inserted to the package
+ if ( pElement->m_bIsInserted )
+ {
+ pElement->m_aOriginalName = pair.first;
+
+ if ( pElement->m_bIsStorage )
+ {
+ OSL_ENSURE(pElement->m_xStorage, "An inserted storage is incomplete!");
+ if (!pElement->m_xStorage)
+ throw uno::RuntimeException( THROW_WHERE );
+
+ if (pElement->m_xStorage->m_bCommited)
+ {
+ pElement->m_xStorage->InsertIntoPackageFolder(/*aName*/pair.first, xNewPackageFolder);
+
+ pElement->m_bIsInserted = false;
+ }
+ }
+ else
+ {
+ OSL_ENSURE(pElement->m_xStream, "An inserted stream is incomplete!");
+ if (!pElement->m_xStream)
+ throw uno::RuntimeException( THROW_WHERE );
+
+ if (!pElement->m_xStream->IsTransacted())
+ pElement->m_xStream->Commit();
+
+ if (pElement->m_xStream->m_bFlushed)
+ {
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ CommitStreamRelInfo( /*aName*/pair.first, pElement );
+
+ pElement->m_xStream->InsertIntoPackageFolder( /*aName*/pair.first, xNewPackageFolder );
+
+ pElement->m_bIsInserted = false;
+ }
+ }
+ }
+ }
+
+ if ( m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ // move properties to the destination package folder
+ uno::Reference< beans::XPropertySet > xProps( xNewPackageFolder, uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue( "MediaType", uno::Any( m_aMediaType ) );
+ xProps->setPropertyValue( "Version", uno::Any( m_aVersion ) );
+ }
+
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ CommitRelInfo( xNewPackageFolder ); // store own relations and commit complete relations storage
+
+ if ( m_bIsRoot )
+ {
+ uno::Reference< util::XChangesBatch > xChangesBatch( m_xPackage, uno::UNO_QUERY_THROW );
+ try
+ {
+ xChangesBatch->commitChanges();
+ }
+ catch( const lang::WrappedTargetException& r )
+ {
+ css::uno::Any ex( cppu::getCaughtException() );
+ // the wrapped UseBackupException means that the target medium can be corrupted
+ embed::UseBackupException aException;
+ if ( r.TargetException >>= aException )
+ {
+ m_xStream.clear();
+ m_xInputStream.clear();
+ throw aException;
+ }
+
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(ex));
+ throw;
+ }
+ }
+ else if ( !m_bCommited )
+ {
+ m_xPackageFolder = xNewPackageFolder;
+ m_bCommited = true;
+ }
+
+ // after commit the mediatype treated as the correct one
+ m_bMTFallbackUsed = false;
+}
+
+void OStorage_Impl::Revert()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !( m_nStorageMode & embed::ElementModes::WRITE ) )
+ return; // nothing to do
+
+ // all the children must be removed
+ // they will be created later on demand
+
+ // rebuild the map - cannot do it in-place, because we're changing some of the key values
+ std::unordered_map<OUString, std::vector<SotElement_Impl*>> oldMap;
+ std::swap(oldMap, m_aChildrenMap);
+
+ for (const auto & rPair : oldMap)
+ for (auto pElement : rPair.second)
+ {
+ if ( pElement->m_bIsInserted )
+ delete pElement;
+ else
+ {
+ ClearElement( pElement );
+
+ pElement->m_bIsRemoved = false;
+
+ m_aChildrenMap[pElement->m_aOriginalName].push_back(pElement);
+ }
+ }
+
+ // return replaced removed elements
+ for ( auto& pDeleted : m_aDeletedVector )
+ {
+ m_aChildrenMap[pDeleted->m_aOriginalName].push_back(pDeleted);
+
+ ClearElement( pDeleted );
+
+ pDeleted->m_bIsRemoved = false;
+ }
+ m_aDeletedVector.clear();
+
+ m_bControlMediaType = false;
+ m_bControlVersion = false;
+
+ GetStorageProperties();
+
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ // currently the relations storage is changed only on commit
+ m_xNewRelInfoStream.clear();
+ m_aRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
+ m_nRelInfoStatus = RELINFO_NO_INIT;
+ }
+}
+
+::comphelper::SequenceAsHashMap OStorage_Impl::GetCommonRootEncryptionData()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
+
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw packages::NoEncryptionException( THROW_WHERE );
+
+ if ( m_bIsRoot )
+ {
+ if ( !m_bHasCommonEncryptionData )
+ throw packages::NoEncryptionException( THROW_WHERE );
+
+ return m_aCommonEncryptionData;
+ }
+ else
+ {
+ if ( !m_pParent )
+ throw packages::NoEncryptionException( THROW_WHERE );
+
+ return m_pParent->GetCommonRootEncryptionData();
+ }
+}
+
+SotElement_Impl* OStorage_Impl::FindElement( const OUString& rName )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ SAL_WARN_IF( rName.isEmpty(), "package.xstor", "Name is empty!" );
+
+ ReadContents();
+
+ auto mapIt = m_aChildrenMap.find(rName);
+ if (mapIt == m_aChildrenMap.end() && m_bRepairPackage)
+ mapIt = std::find_if(m_aChildrenMap.begin(), m_aChildrenMap.end(),
+ [&rName](const auto& pair)
+ { return rName.equalsIgnoreAsciiCase(pair.first); });
+ if (mapIt == m_aChildrenMap.end())
+ return nullptr;
+ for (auto pElement : mapIt->second)
+ if (!pElement->m_bIsRemoved)
+ return pElement;
+
+ return nullptr;
+}
+
+SotElement_Impl* OStorage_Impl::InsertStream( const OUString& aName, bool bEncr )
+{
+ SAL_WARN_IF( !m_xPackage.is(), "package.xstor", "Not possible to refer to package as to factory!" );
+ if ( !m_xPackage.is() )
+ throw embed::InvalidStorageException( THROW_WHERE);
+
+ uno::Sequence< uno::Any > aSeq{ uno::Any(false) };
+ uno::Reference< uno::XInterface > xNewElement( m_xPackage->createInstanceWithArguments( aSeq ) );
+
+ SAL_WARN_IF( !xNewElement.is(), "package.xstor", "Not possible to create a new stream!" );
+ if ( !xNewElement.is() )
+ throw io::IOException( THROW_WHERE );
+
+ uno::Reference< packages::XDataSinkEncrSupport > xPackageSubStream( xNewElement, uno::UNO_QUERY_THROW );
+
+ OSL_ENSURE( m_nStorageType == embed::StorageFormats::PACKAGE || !bEncr, "Only package storage supports encryption!" );
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE && bEncr )
+ throw packages::NoEncryptionException( THROW_WHERE );
+
+ // the mode is not needed for storage stream internal implementation
+ SotElement_Impl* pNewElement = InsertElement( aName, false );
+ pNewElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, bEncr, m_nStorageType, true));
+
+ m_aChildrenMap[aName].push_back( pNewElement );
+ m_bIsModified = true;
+ m_bBroadcastModified = true;
+
+ return pNewElement;
+}
+
+void OStorage_Impl::InsertRawStream( const OUString& aName, const uno::Reference< io::XInputStream >& xInStream )
+{
+ // insert of raw stream means insert and commit
+ SAL_WARN_IF( !m_xPackage.is(), "package.xstor", "Not possible to refer to package as to factory!" );
+ if ( !m_xPackage.is() )
+ throw embed::InvalidStorageException( THROW_WHERE );
+
+ if ( m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw packages::NoEncryptionException( THROW_WHERE );
+
+ uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY );
+ uno::Reference< io::XInputStream > xInStrToInsert = xSeek.is() ? xInStream :
+ GetSeekableTempCopy( xInStream );
+
+ uno::Sequence< uno::Any > aSeq{ uno::Any(false) };
+ uno::Reference< uno::XInterface > xNewElement( m_xPackage->createInstanceWithArguments( aSeq ) );
+
+ SAL_WARN_IF( !xNewElement.is(), "package.xstor", "Not possible to create a new stream!" );
+ if ( !xNewElement.is() )
+ throw io::IOException( THROW_WHERE );
+
+ uno::Reference< packages::XDataSinkEncrSupport > xPackageSubStream( xNewElement, uno::UNO_QUERY_THROW );
+ xPackageSubStream->setRawStream( xInStrToInsert );
+
+ // the mode is not needed for storage stream internal implementation
+ SotElement_Impl* pNewElement = InsertElement( aName, false );
+ pNewElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, true, m_nStorageType, false));
+ // the stream is inserted and must be treated as a committed one
+ pNewElement->m_xStream->SetToBeCommited();
+
+ m_aChildrenMap[aName].push_back( pNewElement );
+ m_bIsModified = true;
+ m_bBroadcastModified = true;
+}
+
+std::unique_ptr<OStorage_Impl> OStorage_Impl::CreateNewStorageImpl( sal_Int32 nStorageMode )
+{
+ SAL_WARN_IF( !m_xPackage.is(), "package.xstor", "Not possible to refer to package as to factory!" );
+ if ( !m_xPackage.is() )
+ throw embed::InvalidStorageException( THROW_WHERE );
+
+ uno::Sequence< uno::Any > aSeq{ uno::Any(true) };
+ uno::Reference< uno::XInterface > xNewElement( m_xPackage->createInstanceWithArguments( aSeq ) );
+
+ SAL_WARN_IF( !xNewElement.is(), "package.xstor", "Not possible to create a new storage!" );
+ if ( !xNewElement.is() )
+ throw io::IOException( THROW_WHERE );
+
+ uno::Reference< container::XNameContainer > xPackageSubFolder( xNewElement, uno::UNO_QUERY_THROW );
+ std::unique_ptr<OStorage_Impl> pResult(
+ new OStorage_Impl( this, nStorageMode, xPackageSubFolder, m_xPackage, m_xContext, m_nStorageType ));
+ pResult->m_bIsModified = true;
+
+ return pResult;
+}
+
+SotElement_Impl* OStorage_Impl::InsertStorage( const OUString& aName, sal_Int32 nStorageMode )
+{
+ SotElement_Impl* pNewElement = InsertElement( aName, true );
+
+ pNewElement->m_xStorage = CreateNewStorageImpl(nStorageMode);
+
+ m_aChildrenMap[aName].push_back( pNewElement );
+
+ return pNewElement;
+}
+
+SotElement_Impl* OStorage_Impl::InsertElement( const OUString& aName, bool bIsStorage )
+{
+ assert( FindElement(aName) == nullptr && "Should not try to insert existing element");
+
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ SotElement_Impl* pDeletedElm = nullptr;
+
+ auto it = m_aChildrenMap.find(aName);
+ if (it != m_aChildrenMap.end())
+ for (auto pElement : it->second)
+ {
+ SAL_WARN_IF( !pElement->m_bIsRemoved, "package.xstor", "Try to insert an element instead of existing one!" );
+ if ( pElement->m_bIsRemoved )
+ {
+ SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor", "Inserted elements must be deleted immediately!" );
+ pDeletedElm = pElement;
+ }
+ }
+
+ if ( pDeletedElm )
+ {
+ if ( pDeletedElm->m_bIsStorage )
+ OpenSubStorage( pDeletedElm, embed::ElementModes::READWRITE );
+ else
+ OpenSubStream( pDeletedElm );
+
+ auto & rVec = m_aChildrenMap[aName];
+ std::erase(rVec, pDeletedElm);
+ if (rVec.empty())
+ m_aChildrenMap.erase(aName);
+ m_aDeletedVector.push_back( pDeletedElm );
+ }
+
+ // create new element
+ return new SotElement_Impl( aName, bIsStorage, true );
+}
+
+void OStorage_Impl::OpenSubStorage( SotElement_Impl* pElement, sal_Int32 nStorageMode )
+{
+ SAL_WARN_IF( !pElement, "package.xstor", "pElement is not set!" );
+ SAL_WARN_IF( !pElement->m_bIsStorage, "package.xstor", "Storage flag is not set!" );
+
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if (!pElement->m_xStorage)
+ {
+ SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor", "Inserted element must be created already!" );
+
+ uno::Reference< uno::XInterface > xTmp;
+ m_xPackageFolder->getByName( pElement->m_aOriginalName ) >>= xTmp;
+ if ( !xTmp.is() )
+ throw container::NoSuchElementException( THROW_WHERE );
+
+ uno::Reference< container::XNameContainer > xPackageSubFolder( xTmp, uno::UNO_QUERY_THROW );
+ pElement->m_xStorage.reset(new OStorage_Impl(this, nStorageMode, xPackageSubFolder, m_xPackage, m_xContext, m_nStorageType));
+ }
+}
+
+void OStorage_Impl::OpenSubStream( SotElement_Impl* pElement )
+{
+ SAL_WARN_IF( !pElement, "package.xstor", "pElement is not set!" );
+ SAL_WARN_IF( pElement->m_bIsStorage, "package.xstor", "Storage flag is set!" );
+
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if (pElement->m_xStream)
+ return;
+
+ SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor", "Inserted element must be created already!" );
+
+ uno::Reference< uno::XInterface > xTmp;
+ m_xPackageFolder->getByName( pElement->m_aOriginalName ) >>= xTmp;
+ if ( !xTmp.is() )
+ throw container::NoSuchElementException( THROW_WHERE );
+
+ uno::Reference< packages::XDataSinkEncrSupport > xPackageSubStream( xTmp, uno::UNO_QUERY_THROW );
+
+ // the stream can never be inserted here, because inserted stream element holds the stream till commit or destruction
+ pElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, false, m_nStorageType, false, GetRelInfoStreamForName(pElement->m_aOriginalName)));
+}
+
+uno::Sequence< OUString > OStorage_Impl::GetElementNames()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ ReadContents();
+
+ sal_Int32 nCnt = 0;
+ for ( const auto& pair : m_aChildrenMap )
+ for (auto pElement : pair.second)
+ {
+ if ( !pElement->m_bIsRemoved )
+ nCnt++;
+ }
+
+ uno::Sequence<OUString> aElementNames(nCnt);
+ OUString* pArray = aElementNames.getArray();
+ for ( const auto& pair : m_aChildrenMap )
+ for (auto pElement : pair.second)
+ {
+ if ( !pElement->m_bIsRemoved )
+ *pArray++ = pair.first;
+ }
+
+ return aElementNames;
+}
+
+void OStorage_Impl::RemoveElement( OUString const & rName, SotElement_Impl* pElement )
+{
+ assert(pElement);
+
+ if ( (pElement->m_xStorage && ( pElement->m_xStorage->m_pAntiImpl || !pElement->m_xStorage->m_aReadOnlyWrapVector.empty() ))
+ || (pElement->m_xStream && ( pElement->m_xStream->m_pAntiImpl || !pElement->m_xStream->m_aInputStreamsVector.empty() )) )
+ throw io::IOException( THROW_WHERE ); // TODO: Access denied
+
+ auto mapIt = m_aChildrenMap.find(rName);
+ for (auto it = mapIt->second.begin(); it != mapIt->second.end(); ++it)
+ if (pElement == *it)
+ {
+ if ( pElement->m_bIsInserted )
+ {
+ delete pElement;
+ std::erase(mapIt->second, pElement);
+ if (mapIt->second.empty())
+ m_aChildrenMap.erase(mapIt);
+ }
+ else
+ {
+ pElement->m_bIsRemoved = true;
+ ClearElement( pElement );
+ }
+ return;
+ }
+ assert(false && "not found");
+
+ // TODO/OFOPXML: the rel stream should be removed as well
+}
+
+void OStorage_Impl::ClearElement( SotElement_Impl* pElement )
+{
+ pElement->m_xStorage.reset();
+ pElement->m_xStream.reset();
+}
+
+void OStorage_Impl::CloneStreamElement( const OUString& aStreamName,
+ bool bEncryptionDataProvided,
+ const ::comphelper::SequenceAsHashMap& aEncryptionData,
+ uno::Reference< io::XStream >& xTargetStream )
+{
+ SotElement_Impl *pElement = FindElement( aStreamName );
+ if ( !pElement )
+ {
+ // element does not exist, throw exception
+ throw io::IOException( THROW_WHERE ); // TODO: access_denied
+ }
+ else if ( pElement->m_bIsStorage )
+ throw io::IOException( THROW_WHERE );
+
+ if (!pElement->m_xStream)
+ OpenSubStream( pElement );
+
+ if (!pElement->m_xStream || !pElement->m_xStream->m_xPackageStream.is())
+ throw io::IOException( THROW_WHERE ); // TODO: general_error
+
+ // the existence of m_pAntiImpl of the child is not interesting,
+ // the copy will be created internally
+
+ // usual copying is not applicable here, only last flushed version of the
+ // child stream should be used for copying. Probably the children m_xPackageStream
+ // can be used as a base of a new stream, that would be copied to result
+ // storage. The only problem is that some package streams can be accessed from outside
+ // at the same time (now solved by wrappers that remember own position).
+
+ if (bEncryptionDataProvided)
+ pElement->m_xStream->GetCopyOfLastCommit(xTargetStream, aEncryptionData);
+ else
+ pElement->m_xStream->GetCopyOfLastCommit(xTargetStream);
+}
+
+void OStorage_Impl::RemoveStreamRelInfo( std::u16string_view aOriginalName )
+{
+ // this method should be used only in OStorage_Impl::Commit() method
+ // the aOriginalName can be empty, in this case the storage relation info should be removed
+
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML && m_xRelStorage.is() )
+ {
+ OUString aRelStreamName = OUString::Concat(aOriginalName) + ".rels";
+
+ if ( m_xRelStorage->hasByName( aRelStreamName ) )
+ m_xRelStorage->removeElement( aRelStreamName );
+ }
+}
+
+void OStorage_Impl::CreateRelStorage()
+{
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ return;
+
+ if ( m_xRelStorage.is() )
+ return;
+
+ if ( !m_pRelStorElement )
+ {
+ m_pRelStorElement = new SotElement_Impl( "_rels", true, true );
+ m_pRelStorElement->m_xStorage = CreateNewStorageImpl(embed::ElementModes::WRITE);
+ if (m_pRelStorElement->m_xStorage)
+ m_pRelStorElement->m_xStorage->m_pParent = nullptr; // the relation storage is completely controlled by parent
+ }
+
+ if (!m_pRelStorElement->m_xStorage)
+ OpenSubStorage( m_pRelStorElement, embed::ElementModes::WRITE );
+
+ if (!m_pRelStorElement->m_xStorage)
+ throw uno::RuntimeException( THROW_WHERE );
+
+ m_xRelStorage = new OStorage(m_pRelStorElement->m_xStorage.get(), false);
+}
+
+void OStorage_Impl::CommitStreamRelInfo( std::u16string_view rName, SotElement_Impl const * pStreamElement )
+{
+ // this method should be used only in OStorage_Impl::Commit() method
+
+ // the stream element must be provided
+ if ( !pStreamElement )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ if (m_nStorageType == embed::StorageFormats::OFOPXML && pStreamElement->m_xStream)
+ {
+ SAL_WARN_IF( rName.empty(), "package.xstor", "The name must not be empty!" );
+
+ if ( !m_xRelStorage.is() )
+ {
+ // Create new rels storage, this is commit scenario so it must be possible
+ CreateRelStorage();
+ }
+
+ pStreamElement->m_xStream->CommitStreamRelInfo(m_xRelStorage, pStreamElement->m_aOriginalName, rName);
+ }
+}
+
+uno::Reference< io::XInputStream > OStorage_Impl::GetRelInfoStreamForName(
+ std::u16string_view aName )
+{
+ if ( m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ ReadContents();
+ if ( m_xRelStorage.is() )
+ {
+ OUString aRelStreamName = OUString::Concat(aName) + ".rels";
+ if ( m_xRelStorage->hasByName( aRelStreamName ) )
+ {
+ uno::Reference< io::XStream > xStream = m_xRelStorage->openStreamElement( aRelStreamName, embed::ElementModes::READ );
+ if ( xStream.is() )
+ return xStream->getInputStream();
+ }
+ }
+ }
+
+ return uno::Reference< io::XInputStream >();
+}
+
+void OStorage_Impl::CommitRelInfo( const uno::Reference< container::XNameContainer >& xNewPackageFolder )
+{
+ // this method should be used only in OStorage_Impl::Commit() method
+ OUString aRelsStorName("_rels");
+
+ if ( !xNewPackageFolder.is() )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ if ( m_nStorageType != embed::StorageFormats::OFOPXML )
+ return;
+
+ if ( m_nRelInfoStatus == RELINFO_BROKEN || m_nRelInfoStatus == RELINFO_CHANGED_BROKEN )
+ throw io::IOException( THROW_WHERE );
+
+ if (m_nRelInfoStatus == RELINFO_CHANGED)
+ {
+ if (m_aRelInfo.hasElements())
+ {
+ CreateRelStorage();
+
+ uno::Reference<io::XStream> xRelsStream = m_xRelStorage->openStreamElement(
+ ".rels", embed::ElementModes::TRUNCATE | embed::ElementModes::READWRITE);
+
+ uno::Reference<io::XOutputStream> xOutStream = xRelsStream->getOutputStream();
+ if (!xOutStream.is())
+ throw uno::RuntimeException(THROW_WHERE);
+
+ ::comphelper::OFOPXMLHelper::WriteRelationsInfoSequence(xOutStream, m_aRelInfo,
+ m_xContext);
+
+ // set the mediatype
+ uno::Reference<beans::XPropertySet> xPropSet(xRelsStream, uno::UNO_QUERY_THROW);
+ xPropSet->setPropertyValue(
+ "MediaType", uno::Any(OUString(
+ "application/vnd.openxmlformats-package.relationships+xml")));
+
+ m_nRelInfoStatus = RELINFO_READ;
+ }
+ else if (m_xRelStorage.is())
+ RemoveStreamRelInfo(std::u16string_view()); // remove own rel info
+ }
+ else if (m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ
+ || m_nRelInfoStatus == RELINFO_CHANGED_STREAM)
+ {
+ CreateRelStorage();
+
+ uno::Reference<io::XStream> xRelsStream = m_xRelStorage->openStreamElement(
+ ".rels", embed::ElementModes::TRUNCATE | embed::ElementModes::READWRITE);
+
+ uno::Reference<io::XOutputStream> xOutputStream = xRelsStream->getOutputStream();
+ if (!xOutputStream.is())
+ throw uno::RuntimeException(THROW_WHERE);
+
+ uno::Reference<io::XSeekable> xSeek(m_xNewRelInfoStream, uno::UNO_QUERY_THROW);
+ xSeek->seek(0);
+ ::comphelper::OStorageHelper::CopyInputToOutput(m_xNewRelInfoStream, xOutputStream);
+
+ // set the mediatype
+ uno::Reference<beans::XPropertySet> xPropSet(xRelsStream, uno::UNO_QUERY_THROW);
+ xPropSet->setPropertyValue(
+ "MediaType",
+ uno::Any(OUString("application/vnd.openxmlformats-package.relationships+xml")));
+
+ m_xNewRelInfoStream.clear();
+ if (m_nRelInfoStatus == RELINFO_CHANGED_STREAM)
+ {
+ m_aRelInfo = uno::Sequence<uno::Sequence<beans::StringPair>>();
+ m_nRelInfoStatus = RELINFO_NO_INIT;
+ }
+ else
+ m_nRelInfoStatus = RELINFO_READ;
+ }
+
+ if ( !m_xRelStorage.is() )
+ return;
+
+ if ( m_xRelStorage->hasElements() )
+ {
+ uno::Reference< embed::XTransactedObject > xTrans( m_xRelStorage, uno::UNO_QUERY_THROW );
+ xTrans->commit();
+ }
+
+ if ( xNewPackageFolder.is() && xNewPackageFolder->hasByName( aRelsStorName ) )
+ xNewPackageFolder->removeByName( aRelsStorName );
+
+ if ( !m_xRelStorage->hasElements() )
+ {
+ // the empty relations storage should not be created
+ delete m_pRelStorElement;
+ m_pRelStorElement = nullptr;
+ m_xRelStorage.clear();
+ }
+ else if ( m_pRelStorElement && m_pRelStorElement->m_xStorage && xNewPackageFolder.is() )
+ m_pRelStorElement->m_xStorage->InsertIntoPackageFolder( aRelsStorName, xNewPackageFolder );
+}
+
+// OStorage implementation
+
+OStorage::OStorage( uno::Reference< io::XInputStream > const & xInputStream,
+ sal_Int32 nMode,
+ const uno::Sequence< beans::PropertyValue >& xProperties,
+ uno::Reference< uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType )
+: m_pImpl( new OStorage_Impl( xInputStream, nMode, xProperties, xContext, nStorageType ) )
+, m_xSharedMutex( m_pImpl->m_xMutex )
+, m_aListenersContainer( m_pImpl->m_xMutex->GetMutex() )
+, m_bReadOnlyWrap( false )
+{
+ m_pImpl->m_pAntiImpl = this;
+}
+
+OStorage::OStorage( uno::Reference< io::XStream > const & xStream,
+ sal_Int32 nMode,
+ const uno::Sequence< beans::PropertyValue >& xProperties,
+ uno::Reference< uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType )
+: m_pImpl( new OStorage_Impl( xStream, nMode, xProperties, xContext, nStorageType ) )
+, m_xSharedMutex( m_pImpl->m_xMutex )
+, m_aListenersContainer( m_pImpl->m_xMutex->GetMutex() )
+, m_bReadOnlyWrap( false )
+{
+ m_pImpl->m_pAntiImpl = this;
+}
+
+OStorage::OStorage( OStorage_Impl* pImpl, bool bReadOnlyWrap )
+: m_pImpl( pImpl )
+, m_xSharedMutex( m_pImpl->m_xMutex )
+, m_aListenersContainer( m_pImpl->m_xMutex->GetMutex() )
+, m_bReadOnlyWrap( bReadOnlyWrap )
+{
+ // this call can be done only from OStorage_Impl implementation to create child storage
+ assert( m_pImpl && m_pImpl->m_xMutex.is() && "The provided pointer & mutex MUST NOT be empty!" );
+
+ OSL_ENSURE( ( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE ) == embed::ElementModes::WRITE ||
+ m_bReadOnlyWrap,
+ "The wrapper can not allow writing in case implementation does not!" );
+
+ if ( !bReadOnlyWrap )
+ m_pImpl->m_pAntiImpl = this;
+}
+
+OStorage::~OStorage()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+ if ( m_pImpl )
+ {
+ osl_atomic_increment(&m_refCount); // to call dispose
+ try {
+ dispose();
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Handled exception");
+ }
+ }
+}
+
+void OStorage::InternalDispose( bool bNotifyImpl )
+{
+ if ( !m_pImpl )
+ return;
+
+ // the source object is also a kind of locker for the current object
+ // since the listeners could dispose the object while being notified
+ lang::EventObject aSource( getXWeak() );
+ m_aListenersContainer.disposeAndClear( aSource );
+
+ if ( !m_pImpl )
+ return;
+
+ m_pImpl->m_nModifiedListenerCount = 0;
+
+ if ( m_bReadOnlyWrap )
+ {
+ OSL_ENSURE( m_aOpenSubComponentsVector.empty() || m_pSubElDispListener,
+ "If any subelements are open the listener must exist!" );
+
+ if (m_pSubElDispListener)
+ {
+ m_pSubElDispListener->OwnerIsDisposed();
+
+ // iterate through m_pData->m_aOpenSubComponentsVector
+ // deregister m_pData->m_pSubElDispListener and dispose all of them
+ if ( !m_aOpenSubComponentsVector.empty() )
+ {
+ for ( const auto& pComp : m_aOpenSubComponentsVector )
+ {
+ uno::Reference< lang::XComponent > xTmp = pComp;
+ if ( xTmp.is() )
+ {
+ xTmp->removeEventListener( uno::Reference< lang::XEventListener >(
+ static_cast< lang::XEventListener* >( m_pSubElDispListener.get())));
+
+ try {
+ xTmp->dispose();
+ } catch( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
+ }
+ }
+ }
+
+ m_aOpenSubComponentsVector.clear();
+ }
+ }
+
+ if ( bNotifyImpl )
+ m_pImpl->RemoveReadOnlyWrap( *this );
+ }
+ else
+ {
+ m_pImpl->m_pAntiImpl = nullptr;
+
+ if ( bNotifyImpl )
+ {
+ if ( m_pImpl->m_bIsRoot )
+ delete m_pImpl;
+ else
+ {
+ // the non-committed changes for the storage must be removed
+ m_pImpl->Revert();
+ }
+ }
+ }
+
+ m_pImpl = nullptr;
+}
+
+void OStorage::ChildIsDisposed( const uno::Reference< uno::XInterface >& xChild )
+{
+ // this method can only be called by child disposing listener
+
+ // this method must not contain any locking
+ // the locking is done in the listener
+
+ auto& rVec = m_aOpenSubComponentsVector;
+ std::erase_if(rVec,
+ [&xChild](const uno::Reference<lang::XComponent>& xTmp) {
+ return !xTmp.is() || xTmp == xChild;
+ });
+}
+
+void OStorage::BroadcastModifiedIfNecessary()
+{
+ // no need to lock mutex here for the checking of m_pImpl, and m_pData is alive until the object is destructed
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( !m_pImpl->m_bBroadcastModified )
+ return;
+
+ m_pImpl->m_bBroadcastModified = false;
+
+ SAL_WARN_IF( m_bReadOnlyWrap, "package.xstor", "The storage can not be modified at all!" );
+
+ lang::EventObject aSource( getXWeak() );
+
+ comphelper::OInterfaceContainerHelper2* pContainer =
+ m_aListenersContainer.getContainer(
+ cppu::UnoType<util::XModifyListener>::get());
+ if ( pContainer )
+ {
+ comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer );
+ while ( pIterator.hasMoreElements( ) )
+ {
+ static_cast<util::XModifyListener*>( pIterator.next( ) )->modified( aSource );
+ }
+ }
+}
+
+void OStorage::BroadcastTransaction( sal_Int8 nMessage )
+/*
+ 1 - preCommit
+ 2 - committed
+ 3 - preRevert
+ 4 - reverted
+*/
+{
+ // no need to lock mutex here for the checking of m_pImpl, and m_pData is alive until the object is destructed
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ SAL_WARN_IF( m_bReadOnlyWrap, "package.xstor", "The storage can not be modified at all!" );
+
+ lang::EventObject aSource( getXWeak() );
+
+ comphelper::OInterfaceContainerHelper2* pContainer =
+ m_aListenersContainer.getContainer(
+ cppu::UnoType<embed::XTransactionListener>::get());
+ if ( !pContainer )
+ return;
+
+ comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer );
+ while ( pIterator.hasMoreElements( ) )
+ {
+ OSL_ENSURE( nMessage >= 1 && nMessage <= 4, "Wrong internal notification code is used!" );
+
+ switch( nMessage )
+ {
+ case STOR_MESS_PRECOMMIT:
+ static_cast<embed::XTransactionListener*>( pIterator.next( ) )->preCommit( aSource );
+ break;
+ case STOR_MESS_COMMITTED:
+ static_cast<embed::XTransactionListener*>( pIterator.next( ) )->commited( aSource );
+ break;
+ case STOR_MESS_PREREVERT:
+ static_cast<embed::XTransactionListener*>( pIterator.next( ) )->preRevert( aSource );
+ break;
+ case STOR_MESS_REVERTED:
+ static_cast<embed::XTransactionListener*>( pIterator.next( ) )->reverted( aSource );
+ break;
+ }
+ }
+}
+
+SotElement_Impl* OStorage::OpenStreamElement_Impl( const OUString& aStreamName, sal_Int32 nOpenMode, bool bEncr )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ OSL_ENSURE( !m_bReadOnlyWrap || ( nOpenMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE,
+ "An element can not be opened for writing in readonly storage!" );
+
+ SotElement_Impl *pElement = m_pImpl->FindElement( aStreamName );
+ if ( !pElement )
+ {
+ // element does not exist, check if creation is allowed
+ if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE )
+ || (( nOpenMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE )
+ || ( nOpenMode & embed::ElementModes::NOCREATE ) == embed::ElementModes::NOCREATE )
+ {
+ throw io::IOException("Element does not exist and cannot be "
+ "created: \"" + aStreamName + "\""); // TODO: access_denied
+ }
+
+ // create a new StreamElement and insert it into the list
+ pElement = m_pImpl->InsertStream( aStreamName, bEncr );
+ }
+ else if ( pElement->m_bIsStorage )
+ {
+ throw io::IOException( THROW_WHERE );
+ }
+
+ SAL_WARN_IF( !pElement, "package.xstor", "In case element can not be created an exception must be thrown!" );
+
+ if (!pElement->m_xStream)
+ m_pImpl->OpenSubStream( pElement );
+
+ if (!pElement->m_xStream)
+ throw io::IOException( THROW_WHERE );
+
+ return pElement;
+}
+
+void OStorage::MakeLinkToSubComponent_Impl( const uno::Reference< lang::XComponent >& xComponent )
+{
+ if ( !xComponent.is() )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ if (!m_pSubElDispListener)
+ {
+ m_pSubElDispListener = new OChildDispListener_Impl( *this );
+ }
+
+ xComponent->addEventListener( m_pSubElDispListener );
+
+ m_aOpenSubComponentsVector.emplace_back(xComponent );
+}
+
+// XInterface
+
+uno::Any SAL_CALL OStorage::queryInterface( const uno::Type& rType )
+{
+ // common interfaces
+ uno::Any aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<lang::XTypeProvider*> ( this )
+ , static_cast<embed::XStorage*> ( this )
+ , static_cast<embed::XStorage2*> ( this )
+ , static_cast<embed::XTransactedObject*> ( this )
+ , static_cast<embed::XTransactionBroadcaster*> ( this )
+ , static_cast<util::XModifiable*> ( this )
+ , static_cast<container::XNameAccess*> ( this )
+ , static_cast<container::XElementAccess*> ( this )
+ , static_cast<lang::XComponent*> ( this )
+ , static_cast<beans::XPropertySet*> ( this )
+ , static_cast<embed::XOptimizedStorage*> ( this ) );
+
+ if ( aReturn.hasValue() )
+ return aReturn ;
+
+ aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<embed::XHierarchicalStorageAccess*> ( this )
+ , static_cast<embed::XHierarchicalStorageAccess2*> ( this ) );
+
+ if ( aReturn.hasValue() )
+ return aReturn ;
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ if ( m_pImpl->m_bIsRoot )
+ {
+ aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<embed::XStorageRawAccess*> ( this )
+ , static_cast<embed::XEncryptionProtectedSource*> ( this )
+ , static_cast<embed::XEncryptionProtectedSource2*> ( this )
+ , static_cast<embed::XEncryptionProtectedStorage*> ( this ) );
+ }
+ else
+ {
+ aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<embed::XStorageRawAccess*> ( this ) );
+ }
+ }
+ else if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ aReturn = ::cppu::queryInterface
+ ( rType
+ , static_cast<embed::XRelationshipAccess*> ( this ) );
+ }
+
+ if ( aReturn.hasValue() )
+ return aReturn ;
+
+ return OWeakObject::queryInterface( rType );
+}
+
+void SAL_CALL OStorage::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL OStorage::release() noexcept
+{
+ OWeakObject::release();
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL OStorage::getTypes()
+{
+ if (! m_oTypeCollection)
+ {
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if (! m_oTypeCollection)
+ {
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ if ( m_pImpl->m_bIsRoot )
+ {
+ m_oTypeCollection.emplace(
+ cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<embed::XStorage>::get()
+ , cppu::UnoType<embed::XStorage2>::get()
+ , cppu::UnoType<embed::XStorageRawAccess>::get()
+ , cppu::UnoType<embed::XTransactedObject>::get()
+ , cppu::UnoType<embed::XTransactionBroadcaster>::get()
+ , cppu::UnoType<util::XModifiable>::get()
+ , cppu::UnoType<embed::XEncryptionProtectedStorage>::get()
+ , cppu::UnoType<embed::XEncryptionProtectedSource2>::get()
+ , cppu::UnoType<embed::XEncryptionProtectedSource>::get()
+ , cppu::UnoType<beans::XPropertySet>::get());
+ }
+ else
+ {
+ m_oTypeCollection.emplace(
+ cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<embed::XStorage>::get()
+ , cppu::UnoType<embed::XStorage2>::get()
+ , cppu::UnoType<embed::XStorageRawAccess>::get()
+ , cppu::UnoType<embed::XTransactedObject>::get()
+ , cppu::UnoType<embed::XTransactionBroadcaster>::get()
+ , cppu::UnoType<util::XModifiable>::get()
+ , cppu::UnoType<beans::XPropertySet>::get());
+ }
+ }
+ else if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ m_oTypeCollection.emplace(
+ cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<embed::XStorage>::get()
+ , cppu::UnoType<embed::XTransactedObject>::get()
+ , cppu::UnoType<embed::XTransactionBroadcaster>::get()
+ , cppu::UnoType<util::XModifiable>::get()
+ , cppu::UnoType<embed::XRelationshipAccess>::get()
+ , cppu::UnoType<beans::XPropertySet>::get());
+ }
+ else
+ {
+ m_oTypeCollection.emplace(
+ cppu::UnoType<lang::XTypeProvider>::get()
+ , cppu::UnoType<embed::XStorage>::get()
+ , cppu::UnoType<embed::XTransactedObject>::get()
+ , cppu::UnoType<embed::XTransactionBroadcaster>::get()
+ , cppu::UnoType<util::XModifiable>::get()
+ , cppu::UnoType<beans::XPropertySet>::get());
+ }
+ }
+ }
+
+ return m_oTypeCollection->getTypes() ;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL OStorage::getImplementationId()
+{
+ static const comphelper::UnoIdInit lcl_ImplId;
+ return lcl_ImplId.getSeq();
+}
+
+// XStorage
+void SAL_CALL OStorage::copyToStorage( const uno::Reference< embed::XStorage >& xDest )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( !xDest.is() || xDest == getXWeak() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ try {
+ m_pImpl->CopyToStorage( xDest, false );
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't copy storage!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+uno::Reference< io::XStream > SAL_CALL OStorage::openStreamElement(
+ const OUString& aStreamName, sal_Int32 nOpenMode )
+{
+ osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStreamName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable element name
+
+ if ( ( nOpenMode & embed::ElementModes::WRITE ) && m_bReadOnlyWrap )
+ throw io::IOException( THROW_WHERE ); // TODO: access denied
+
+ uno::Reference< io::XStream > xResult;
+ try
+ {
+ SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamName, nOpenMode, false );
+ OSL_ENSURE(pElement && pElement->m_xStream, "In case element can not be created an exception must be thrown!");
+
+ xResult = pElement->m_xStream->GetStream(nOpenMode, false);
+ SAL_WARN_IF( !xResult.is(), "package.xstor", "The method must throw exception instead of removing empty result!" );
+
+ if ( m_bReadOnlyWrap )
+ {
+ // before the storage disposes the stream it must deregister itself as listener
+ uno::Reference< lang::XComponent > xStreamComponent( xResult, uno::UNO_QUERY_THROW );
+ MakeLinkToSubComponent_Impl( xStreamComponent );
+ }
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException(THROW_WHERE "Can't open stream element!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+ aGuard.clear();
+
+ BroadcastModifiedIfNecessary();
+
+ return xResult;
+}
+
+uno::Reference< io::XStream > SAL_CALL OStorage::openEncryptedStreamElement(
+ const OUString& aStreamName, sal_Int32 nOpenMode, const OUString& aPass )
+{
+ return openEncryptedStream( aStreamName, nOpenMode, ::comphelper::OStorageHelper::CreatePackageEncryptionData( aPass ) );
+}
+
+uno::Reference< embed::XStorage > SAL_CALL OStorage::openStorageElement(
+ const OUString& aStorName, sal_Int32 nStorageMode )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aStorName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStorName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStorName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable storage name
+
+ if ( ( nStorageMode & embed::ElementModes::WRITE ) && m_bReadOnlyWrap )
+ throw io::IOException( THROW_WHERE ); // TODO: access denied
+
+ if ( ( nStorageMode & embed::ElementModes::TRUNCATE )
+ && !( nStorageMode & embed::ElementModes::WRITE ) )
+ throw io::IOException( THROW_WHERE ); // TODO: access denied
+
+ // it's always possible to read written storage in this implementation
+ nStorageMode |= embed::ElementModes::READ;
+
+ uno::Reference< embed::XStorage > xResult;
+ try
+ {
+ SotElement_Impl *pElement = m_pImpl->FindElement( aStorName );
+ if ( !pElement )
+ {
+ // element does not exist, check if creation is allowed
+ if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE )
+ || (( nStorageMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE )
+ || ( nStorageMode & embed::ElementModes::NOCREATE ) == embed::ElementModes::NOCREATE )
+ throw io::IOException( THROW_WHERE ); // TODO: access_denied
+
+ // create a new StorageElement and insert it into the list
+ pElement = m_pImpl->InsertStorage( aStorName, nStorageMode );
+ }
+ else if ( !pElement->m_bIsStorage )
+ {
+ throw io::IOException( THROW_WHERE );
+ }
+ else if (pElement->m_xStorage)
+ {
+ // storage has already been opened; it may be opened another time, if it the mode allows to do so
+ if (pElement->m_xStorage->m_pAntiImpl)
+ {
+ throw io::IOException( THROW_WHERE ); // TODO: access_denied
+ }
+ else if ( !pElement->m_xStorage->m_aReadOnlyWrapVector.empty()
+ && ( nStorageMode & embed::ElementModes::WRITE ) )
+ {
+ throw io::IOException( THROW_WHERE ); // TODO: access_denied
+ }
+ else
+ {
+ // in case parent storage allows writing the readonly mode of the child storage is
+ // virtual, that means that it is just enough to change the flag to let it be writable
+ // and since there is no AntiImpl nobody should be notified about it
+ pElement->m_xStorage->m_nStorageMode = nStorageMode | embed::ElementModes::READ;
+
+ if ( nStorageMode & embed::ElementModes::TRUNCATE )
+ {
+ for (const auto & rPair : pElement->m_xStorage->m_aChildrenMap)
+ for (auto pElementToDel : rPair.second)
+ m_pImpl->RemoveElement( /*aName*/rPair.first, pElementToDel );
+ }
+ }
+ }
+
+ if (!pElement->m_xStorage)
+ m_pImpl->OpenSubStorage(pElement, nStorageMode);
+
+ if (!pElement->m_xStorage)
+ throw io::IOException( THROW_WHERE ); // TODO: general_error
+
+ bool bReadOnlyWrap = ( ( nStorageMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE );
+ rtl::Reference<OStorage> pResultStorage = new OStorage(pElement->m_xStorage.get(), bReadOnlyWrap);
+ xResult = pResultStorage;
+
+ if ( bReadOnlyWrap )
+ {
+ // Before this call is done the object must be refcounted already
+ pElement->m_xStorage->SetReadOnlyWrap(*pResultStorage);
+
+ // before the storage disposes the stream it must deregister itself as listener
+ uno::Reference< lang::XComponent > xStorageComponent( xResult, uno::UNO_QUERY_THROW );
+ MakeLinkToSubComponent_Impl( xStorageComponent );
+ }
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't open storage!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+ return xResult;
+}
+
+uno::Reference< io::XStream > SAL_CALL OStorage::cloneStreamElement( const OUString& aStreamName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStreamName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable storage name
+
+ try
+ {
+ uno::Reference< io::XStream > xResult;
+ m_pImpl->CloneStreamElement( aStreamName, false, ::comphelper::SequenceAsHashMap(), xResult );
+ if ( !xResult.is() )
+ throw uno::RuntimeException( THROW_WHERE );
+ return xResult;
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't clone stream!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+uno::Reference< io::XStream > SAL_CALL OStorage::cloneEncryptedStreamElement(
+ const OUString& aStreamName,
+ const OUString& aPass )
+{
+ return cloneEncryptedStream( aStreamName, ::comphelper::OStorageHelper::CreatePackageEncryptionData( aPass ) );
+}
+
+void SAL_CALL OStorage::copyLastCommitTo(
+ const uno::Reference< embed::XStorage >& xTargetStorage )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ try
+ {
+ m_pImpl->CopyLastCommitTo( xTargetStorage );
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't copy last commit version!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+}
+
+void SAL_CALL OStorage::copyStorageElementLastCommitTo(
+ const OUString& aStorName,
+ const uno::Reference< embed::XStorage >& xTargetStorage )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aStorName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStorName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStorName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable storage name
+
+ try
+ {
+ SotElement_Impl *pElement = m_pImpl->FindElement( aStorName );
+ if ( !pElement )
+ {
+ // element does not exist, throw exception
+ throw io::IOException( THROW_WHERE ); // TODO: access_denied
+ }
+ else if ( !pElement->m_bIsStorage )
+ {
+ throw io::IOException( THROW_WHERE );
+ }
+
+ if (!pElement->m_xStorage)
+ m_pImpl->OpenSubStorage( pElement, embed::ElementModes::READ );
+
+ if (!pElement->m_xStorage)
+ throw io::IOException( THROW_WHERE ); // TODO: general_error
+
+ // the existence of m_pAntiImpl of the child is not interesting,
+ // the copy will be created internally
+
+ pElement->m_xStorage->CopyLastCommitTo(xTargetStorage);
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't copy last commit element version!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+sal_Bool SAL_CALL OStorage::isStreamElement( const OUString& aElementName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aElementName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable name
+
+ SotElement_Impl* pElement = nullptr;
+
+ try
+ {
+ pElement = m_pImpl->FindElement( aElementName );
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can't detect whether it is a stream!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+ if ( !pElement )
+ throw container::NoSuchElementException( THROW_WHERE ); //???
+
+ return !pElement->m_bIsStorage;
+}
+
+sal_Bool SAL_CALL OStorage::isStorageElement( const OUString& aElementName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aElementName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ SotElement_Impl* pElement = nullptr;
+
+ try
+ {
+ pElement = m_pImpl->FindElement( aElementName );
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "can't detect whether it is a storage",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+ if ( !pElement )
+ throw container::NoSuchElementException( THROW_WHERE ); //???
+
+ return pElement->m_bIsStorage;
+}
+
+void SAL_CALL OStorage::removeElement( const OUString& aElementName )
+{
+ {
+ osl::MutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if (!m_pImpl)
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException(THROW_WHERE);
+ }
+
+ if (aElementName.isEmpty()
+ || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aElementName, false))
+ throw lang::IllegalArgumentException(THROW_WHERE "Unexpected entry name syntax.",
+ uno::Reference<uno::XInterface>(), 1);
+
+ if (m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aElementName == "_rels")
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference<uno::XInterface>(),
+ 1); // TODO: unacceptable name
+
+ if (!(m_pImpl->m_nStorageMode & embed::ElementModes::WRITE))
+ throw io::IOException(THROW_WHERE); // TODO: access denied
+
+ try
+ {
+ auto pElement = m_pImpl->FindElement(aElementName);
+ if ( !pElement )
+ throw container::NoSuchElementException(THROW_WHERE); //???
+
+ m_pImpl->RemoveElement(aElementName, pElement);
+
+ m_pImpl->m_bIsModified = true;
+ m_pImpl->m_bBroadcastModified = true;
+ }
+ catch (const embed::InvalidStorageException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const io::IOException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const embed::StorageWrappedTargetException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ uno::Any aCaught(::cppu::getCaughtException());
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException(THROW_WHERE "Can't remove element!",
+ uno::Reference<io::XInputStream>(), aCaught);
+ }
+ }
+
+ BroadcastModifiedIfNecessary();
+}
+
+void SAL_CALL OStorage::renameElement( const OUString& aElementName, const OUString& aNewName )
+{
+ {
+ osl::MutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if (!m_pImpl)
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException(THROW_WHERE);
+ }
+
+ if (aElementName.isEmpty()
+ || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aElementName, false)
+ || aNewName.isEmpty()
+ || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aNewName, false))
+ throw lang::IllegalArgumentException(THROW_WHERE "Unexpected entry name syntax.",
+ uno::Reference<uno::XInterface>(), 1);
+
+ if (m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML
+ && (aElementName == "_rels" || aNewName == "_rels"))
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference<uno::XInterface>(),
+ 0); // TODO: unacceptable element name
+
+ if (!(m_pImpl->m_nStorageMode & embed::ElementModes::WRITE))
+ throw io::IOException(THROW_WHERE); // TODO: access denied
+
+ try
+ {
+ SotElement_Impl* pRefElement = m_pImpl->FindElement(aNewName);
+ if (pRefElement)
+ throw container::ElementExistException(THROW_WHERE); //???
+
+ auto pElement = m_pImpl->FindElement( aElementName );
+ if ( !pElement )
+ throw container::NoSuchElementException(THROW_WHERE); //???
+
+ auto mapIt = m_pImpl->m_aChildrenMap.find(aElementName);
+ auto rVec = mapIt->second;
+ for (auto it = rVec.begin(); it != rVec.end(); ++it)
+ if (pElement == *it)
+ {
+ std::erase(rVec, pElement);
+ if (rVec.empty())
+ m_pImpl->m_aChildrenMap.erase(mapIt);
+ break;
+ }
+ m_pImpl->m_aChildrenMap[aNewName].push_back(pElement);
+ m_pImpl->m_bIsModified = true;
+ m_pImpl->m_bBroadcastModified = true;
+ }
+ catch (const embed::InvalidStorageException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const container::ElementExistException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const io::IOException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const embed::StorageWrappedTargetException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ uno::Any aCaught(::cppu::getCaughtException());
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException(THROW_WHERE "Can't rename element!",
+ uno::Reference<io::XInputStream>(), aCaught);
+ }
+ }
+
+ BroadcastModifiedIfNecessary();
+}
+
+void SAL_CALL OStorage::copyElementTo( const OUString& aElementName,
+ const uno::Reference< embed::XStorage >& xDest,
+ const OUString& aNewName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false )
+ || aNewName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aNewName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( !xDest.is() )
+ // || xDest == getXWeak() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && ( aElementName == "_rels" || aNewName == "_rels" ) )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 ); // unacceptable element name
+
+ try
+ {
+ SotElement_Impl* pElement = m_pImpl->FindElement( aElementName );
+ if ( !pElement )
+ throw container::NoSuchElementException( THROW_WHERE );
+
+ uno::Reference< XNameAccess > xNameAccess( xDest, uno::UNO_QUERY_THROW );
+ if ( xNameAccess->hasByName( aNewName ) )
+ throw container::ElementExistException( THROW_WHERE );
+
+ m_pImpl->CopyStorageElement( pElement, xDest, aNewName, false );
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const container::ElementExistException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't copy element!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+void SAL_CALL OStorage::moveElementTo( const OUString& aElementName,
+ const uno::Reference< embed::XStorage >& xDest,
+ const OUString& aNewName )
+{
+ {
+ osl::MutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if (!m_pImpl)
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException(THROW_WHERE);
+ }
+
+ if (aElementName.isEmpty()
+ || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aElementName, false)
+ || aNewName.isEmpty()
+ || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aNewName, false))
+ throw lang::IllegalArgumentException(THROW_WHERE "Unexpected entry name syntax.",
+ uno::Reference<uno::XInterface>(), 1);
+
+ if (!xDest.is() || xDest == getXWeak())
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference<uno::XInterface>(), 2);
+
+ if (m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML
+ && (aElementName == "_rels" || aNewName == "_rels"))
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference<uno::XInterface>(),
+ 0); // unacceptable element name
+
+ if (!(m_pImpl->m_nStorageMode & embed::ElementModes::WRITE))
+ throw io::IOException(THROW_WHERE); // TODO: access denied
+
+ try
+ {
+ auto pElement = m_pImpl->FindElement( aElementName );
+ if ( !pElement )
+ throw container::NoSuchElementException(THROW_WHERE); //???
+
+ uno::Reference<XNameAccess> xNameAccess(xDest, uno::UNO_QUERY_THROW);
+ if (xNameAccess->hasByName(aNewName))
+ throw container::ElementExistException(THROW_WHERE);
+
+ m_pImpl->CopyStorageElement(pElement, xDest, aNewName, false);
+
+ m_pImpl->RemoveElement(aElementName, pElement);
+
+ m_pImpl->m_bIsModified = true;
+ m_pImpl->m_bBroadcastModified = true;
+ }
+ catch (const embed::InvalidStorageException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const container::ElementExistException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const embed::StorageWrappedTargetException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const io::IOException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ uno::Any aCaught(::cppu::getCaughtException());
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException(THROW_WHERE "Can't move element!",
+ uno::Reference<io::XInputStream>(), aCaught);
+ }
+ }
+
+ BroadcastModifiedIfNecessary();
+}
+
+// XStorage2
+uno::Reference< io::XStream > SAL_CALL OStorage::openEncryptedStream(
+ const OUString& aStreamName, sal_Int32 nOpenMode, const uno::Sequence< beans::NamedValue >& aEncryptionData )
+{
+ osl::ClearableMutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( ( nOpenMode & embed::ElementModes::WRITE ) && m_bReadOnlyWrap )
+ throw io::IOException( THROW_WHERE ); // TODO: access denied
+
+ if ( !aEncryptionData.hasElements() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 3 );
+
+ uno::Reference< io::XStream > xResult;
+ try
+ {
+ SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamName, nOpenMode, true );
+ OSL_ENSURE(pElement && pElement->m_xStream, "In case element can not be created an exception must be thrown!");
+
+ xResult = pElement->m_xStream->GetStream(nOpenMode, aEncryptionData, false);
+ SAL_WARN_IF( !xResult.is(), "package.xstor", "The method must throw exception instead of removing empty result!" );
+
+ if ( m_bReadOnlyWrap )
+ {
+ // before the storage disposes the stream it must deregister itself as listener
+ uno::Reference< lang::XComponent > xStreamComponent( xResult, uno::UNO_QUERY_THROW );
+ MakeLinkToSubComponent_Impl( xStreamComponent );
+ }
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const packages::NoEncryptionException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't open encrypted stream!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+ aGuard.clear();
+
+ BroadcastModifiedIfNecessary();
+
+ return xResult;
+}
+
+uno::Reference< io::XStream > SAL_CALL OStorage::cloneEncryptedStream(
+ const OUString& aStreamName,
+ const uno::Sequence< beans::NamedValue >& aEncryptionData )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( !aEncryptionData.hasElements() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
+
+ try
+ {
+ uno::Reference< io::XStream > xResult;
+ m_pImpl->CloneStreamElement( aStreamName, true, aEncryptionData, xResult );
+ if ( !xResult.is() )
+ throw uno::RuntimeException( THROW_WHERE );
+ return xResult;
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const packages::NoEncryptionException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't clone encrypted stream!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+// XStorageRawAccess
+uno::Reference< io::XInputStream > SAL_CALL OStorage::getPlainRawStreamElement(
+ const OUString& sStreamName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE ); // the interface is not supported and must not be accessible
+
+ if ( sStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( sStreamName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ uno::Reference < io::XInputStream > xTempIn;
+ try
+ {
+ SotElement_Impl* pElement = m_pImpl->FindElement( sStreamName );
+ if ( !pElement )
+ throw container::NoSuchElementException( THROW_WHERE );
+
+ if (!pElement->m_xStream)
+ {
+ m_pImpl->OpenSubStream( pElement );
+ if (!pElement->m_xStream)
+ throw io::IOException( THROW_WHERE );
+ }
+
+ uno::Reference<io::XInputStream> xRawInStream = pElement->m_xStream->GetPlainRawInStream();
+ if ( !xRawInStream.is() )
+ throw io::IOException( THROW_WHERE );
+
+ rtl::Reference < utl::TempFileFastService > xTempFile = new utl::TempFileFastService;
+ uno::Reference < io::XOutputStream > xTempOut = xTempFile->getOutputStream();
+ xTempIn = xTempFile->getInputStream();
+
+ if ( !xTempOut.is() || !xTempIn.is() )
+ throw io::IOException( THROW_WHERE );
+
+ // Copy temporary file to a new one
+ ::comphelper::OStorageHelper::CopyInputToOutput( xRawInStream, xTempOut );
+ xTempOut->closeOutput();
+ xTempFile->seek( 0 );
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't get plain raw stream!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+ return xTempIn;
+}
+
+uno::Reference< io::XInputStream > SAL_CALL OStorage::getRawEncrStreamElement(
+ const OUString& sStreamName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw packages::NoEncryptionException( THROW_WHERE );
+
+ if ( sStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( sStreamName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ uno::Reference < io::XInputStream > xTempIn;
+ try
+ {
+ SotElement_Impl* pElement = m_pImpl->FindElement( sStreamName );
+ if ( !pElement )
+ throw container::NoSuchElementException( THROW_WHERE );
+
+ if (!pElement->m_xStream)
+ {
+ m_pImpl->OpenSubStream( pElement );
+ if (!pElement->m_xStream)
+ throw io::IOException( THROW_WHERE );
+ }
+
+ if (!pElement->m_xStream->IsEncrypted())
+ throw packages::NoEncryptionException( THROW_WHERE );
+
+ uno::Reference< io::XInputStream > xRawInStream = pElement->m_xStream->GetRawInStream();
+ if ( !xRawInStream.is() )
+ throw io::IOException( THROW_WHERE );
+
+ rtl::Reference < utl::TempFileFastService > xTempFile = new utl::TempFileFastService;
+ uno::Reference < io::XOutputStream > xTempOut = xTempFile;
+ xTempIn = xTempFile;
+
+ if ( !xTempFile )
+ throw io::IOException( THROW_WHERE );
+
+ // Copy temporary file to a new one
+ ::comphelper::OStorageHelper::CopyInputToOutput( xRawInStream, xTempOut );
+ xTempFile->closeOutput();
+ xTempFile->seek( 0 );
+
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const packages::NoEncryptionException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't get raw stream!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+ return xTempIn;
+}
+
+void SAL_CALL OStorage::insertRawEncrStreamElement( const OUString& aStreamName,
+ const uno::Reference< io::XInputStream >& xInStream )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw embed::InvalidStorageException( THROW_WHERE );
+
+ if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( !xInStream.is() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
+
+ if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE ) )
+ throw io::IOException( THROW_WHERE ); // TODO: access denied
+
+ try
+ {
+ SotElement_Impl* pElement = m_pImpl->FindElement( aStreamName );
+ if ( pElement )
+ throw container::ElementExistException( THROW_WHERE );
+
+ m_pImpl->InsertRawStream( aStreamName, xInStream );
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const packages::NoRawFormatException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const container::ElementExistException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't insert raw stream!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+// XTransactedObject
+void SAL_CALL OStorage::commit()
+{
+ uno::Reference< util::XModifiable > xParentModif;
+
+ try {
+ BroadcastTransaction( STOR_MESS_PRECOMMIT );
+
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_bReadOnlyWrap )
+ throw io::IOException( THROW_WHERE ); // TODO: access_denied
+
+ m_pImpl->Commit(); // the root storage initiates the storing to source
+
+ // when the storage is committed the parent is modified
+ if ( m_pImpl->m_pParent && m_pImpl->m_pParent->m_pAntiImpl )
+ xParentModif = static_cast<util::XModifiable*>(m_pImpl->m_pParent->m_pAntiImpl);
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Problems on commit!",
+ getXWeak(),
+ aCaught );
+ }
+
+ setModified( false );
+ if ( xParentModif.is() )
+ xParentModif->setModified( true );
+
+ BroadcastTransaction( STOR_MESS_COMMITTED );
+}
+
+void SAL_CALL OStorage::revert()
+{
+ // the method removes all the changes done after last commit
+
+ BroadcastTransaction( STOR_MESS_PREREVERT );
+
+ {
+ osl::MutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if (!m_pImpl)
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException(THROW_WHERE);
+ }
+
+ for (const auto & rPair : m_pImpl->m_aChildrenMap)
+ for (auto pElement : rPair.second)
+ {
+ bool bThrow = (pElement->m_xStorage
+ && (pElement->m_xStorage->m_pAntiImpl
+ || !pElement->m_xStorage->m_aReadOnlyWrapVector.empty()))
+ || (pElement->m_xStream
+ && (pElement->m_xStream->m_pAntiImpl
+ || !pElement->m_xStream->m_aInputStreamsVector.empty()));
+ if (bThrow)
+ throw io::IOException(THROW_WHERE); // TODO: access denied
+ }
+
+ if (m_bReadOnlyWrap || !m_pImpl->m_bListCreated)
+ return; // nothing to do
+
+ try
+ {
+ m_pImpl->Revert();
+ m_pImpl->m_bIsModified = false;
+ m_pImpl->m_bBroadcastModified = true;
+ }
+ catch (const io::IOException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch (const embed::StorageWrappedTargetException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ uno::Any aCaught(::cppu::getCaughtException());
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException(THROW_WHERE "Problems on revert!",
+ getXWeak(),
+ aCaught);
+ }
+ }
+
+ setModified( false );
+ BroadcastTransaction( STOR_MESS_REVERTED );
+}
+
+// XTransactionBroadcaster
+void SAL_CALL OStorage::addTransactionListener( const uno::Reference< embed::XTransactionListener >& aListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ m_aListenersContainer.addInterface( cppu::UnoType<embed::XTransactionListener>::get(),
+ aListener );
+}
+
+void SAL_CALL OStorage::removeTransactionListener( const uno::Reference< embed::XTransactionListener >& aListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ m_aListenersContainer.removeInterface( cppu::UnoType<embed::XTransactionListener>::get(),
+ aListener );
+}
+
+// XModifiable
+// TODO: if there will be no demand on this interface it will be removed from implementation,
+// I do not want to remove it now since it is still possible that it will be inserted
+// to the service back.
+
+sal_Bool SAL_CALL OStorage::isModified()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ return m_pImpl->m_bIsModified;
+}
+
+void SAL_CALL OStorage::setModified( sal_Bool bModified )
+{
+ {
+ osl::MutexGuard aGuard(m_xSharedMutex->GetMutex());
+
+ if (!m_pImpl)
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException(THROW_WHERE);
+ }
+
+ if (m_bReadOnlyWrap)
+ throw beans::PropertyVetoException(THROW_WHERE); // TODO: access denied
+
+ if (m_pImpl->m_bIsModified != bool(bModified))
+ m_pImpl->m_bIsModified = bModified;
+ }
+
+ if ( bModified )
+ {
+ m_pImpl->m_bBroadcastModified = true;
+ BroadcastModifiedIfNecessary();
+ }
+}
+
+void SAL_CALL OStorage::addModifyListener(
+ const uno::Reference< util::XModifyListener >& aListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ osl_atomic_increment( &m_pImpl->m_nModifiedListenerCount );
+ m_aListenersContainer.addInterface(
+ cppu::UnoType<util::XModifyListener>::get(), aListener );
+}
+
+void SAL_CALL OStorage::removeModifyListener(
+ const uno::Reference< util::XModifyListener >& aListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ osl_atomic_decrement( &m_pImpl->m_nModifiedListenerCount );
+ m_aListenersContainer.removeInterface(
+ cppu::UnoType<util::XModifyListener>::get(), aListener );
+}
+
+// XNameAccess
+
+uno::Any SAL_CALL OStorage::getByName( const OUString& aName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable element name
+
+ uno::Any aResult;
+ try
+ {
+ SotElement_Impl* pElement = m_pImpl->FindElement( aName );
+ if ( !pElement )
+ throw container::NoSuchElementException( THROW_WHERE );
+
+ if ( pElement->m_bIsStorage )
+ aResult <<= openStorageElement( aName, embed::ElementModes::READ );
+ else
+ aResult <<= openStreamElement( aName, embed::ElementModes::READ );
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const lang::WrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetException( THROW_WHERE "Can not open storage!",
+ getXWeak(),
+ aCaught );
+ }
+
+ return aResult;
+}
+
+uno::Sequence< OUString > SAL_CALL OStorage::getElementNames()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ try
+ {
+ return m_pImpl->GetElementNames();
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open storage!",
+ getXWeak(),
+ aCaught );
+ }
+}
+
+sal_Bool SAL_CALL OStorage::hasByName( const OUString& aName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aName.isEmpty() )
+ return false;
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aName == "_rels" )
+ return false;
+
+ SotElement_Impl* pElement = nullptr;
+ try
+ {
+ pElement = m_pImpl->FindElement( aName );
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open storage!",
+ getXWeak(),
+ aCaught );
+ }
+
+ return ( pElement != nullptr );
+}
+
+uno::Type SAL_CALL OStorage::getElementType()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ // it is a multitype container
+ return uno::Type();
+}
+
+sal_Bool SAL_CALL OStorage::hasElements()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ try
+ {
+ return m_pImpl->HasChildren();
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open storage!",
+ getXWeak(),
+ aCaught );
+ }
+}
+
+// XComponent
+void SAL_CALL OStorage::dispose()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ try
+ {
+ InternalDispose( true );
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open storage!",
+ getXWeak(),
+ aCaught );
+ }
+}
+
+void SAL_CALL OStorage::addEventListener(
+ const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ m_aListenersContainer.addInterface(
+ cppu::UnoType<lang::XEventListener>::get(), xListener );
+}
+
+void SAL_CALL OStorage::removeEventListener(
+ const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ m_aListenersContainer.removeInterface(
+ cppu::UnoType<lang::XEventListener>::get(), xListener );
+}
+
+// XEncryptionProtectedSource
+
+void SAL_CALL OStorage::setEncryptionPassword( const OUString& aPass )
+{
+ setEncryptionData( ::comphelper::OStorageHelper::CreatePackageEncryptionData( aPass ) );
+}
+
+void SAL_CALL OStorage::removeEncryption()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
+
+ SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "removeEncryption() method is not available for nonroot storages!" );
+ if ( !m_pImpl->m_bIsRoot )
+ return;
+
+ try {
+ m_pImpl->ReadContents();
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ getXWeak(),
+ aCaught );
+ }
+
+ // TODO: check if the password is valid
+ // update all streams that was encrypted with old password
+
+ uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW );
+ try
+ {
+ xPackPropSet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY,
+ uno::Any( uno::Sequence< beans::NamedValue >() ) );
+
+ m_pImpl->m_bHasCommonEncryptionData = false;
+ m_pImpl->m_aCommonEncryptionData.clear();
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_WARN_EXCEPTION( "package.xstor", "The call must not fail, it is pretty simple!" );
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "package.xstor", "The call must not fail, it is pretty simple!" );
+ throw io::IOException( THROW_WHERE );
+ }
+}
+
+// XEncryptionProtectedSource2
+
+void SAL_CALL OStorage::setEncryptionData( const uno::Sequence< beans::NamedValue >& aEncryptionData )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
+
+ if ( !aEncryptionData.hasElements() )
+ throw uno::RuntimeException( THROW_WHERE "Unexpected empty encryption data!" );
+
+ SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "setEncryptionData() method is not available for nonroot storages!" );
+ if ( !m_pImpl->m_bIsRoot )
+ return;
+
+ try {
+ m_pImpl->ReadContents();
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ getXWeak(),
+ aCaught );
+ }
+
+ uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW );
+ try
+ {
+ ::comphelper::SequenceAsHashMap aEncryptionMap( aEncryptionData );
+ xPackPropSet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY,
+ uno::Any( aEncryptionMap.getAsConstNamedValueList() ) );
+
+ m_pImpl->m_bHasCommonEncryptionData = true;
+ m_pImpl->m_aCommonEncryptionData = aEncryptionMap;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:" );
+
+ throw io::IOException( THROW_WHERE );
+ }
+}
+
+sal_Bool SAL_CALL OStorage::hasEncryptionData()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ return m_pImpl && m_pImpl->m_bHasCommonEncryptionData;
+}
+
+// XEncryptionProtectedStorage
+
+void SAL_CALL OStorage::setEncryptionAlgorithms( const uno::Sequence< beans::NamedValue >& aAlgorithms )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
+
+ if ( !aAlgorithms.hasElements() )
+ throw uno::RuntimeException( THROW_WHERE "Unexpected empty encryption algorithms list!" );
+
+ SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "setEncryptionAlgorithms() method is not available for nonroot storages!" );
+ if ( !m_pImpl->m_bIsRoot )
+ return;
+
+ try {
+ m_pImpl->ReadContents();
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ getXWeak(),
+ aCaught );
+ }
+
+ uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW );
+ try
+ {
+ xPackPropSet->setPropertyValue( ENCRYPTION_ALGORITHMS_PROPERTY,
+ uno::Any( aAlgorithms ) );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ getXWeak(),
+ aCaught );
+ }
+}
+
+void SAL_CALL OStorage::setGpgProperties( const uno::Sequence< uno::Sequence< beans::NamedValue > >& aProps )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
+
+ if ( !aProps.hasElements() )
+ throw uno::RuntimeException( THROW_WHERE "Unexpected empty encryption algorithms list!" );
+
+ SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "setGpgProperties() method is not available for nonroot storages!" );
+ if ( !m_pImpl->m_bIsRoot )
+ return;
+
+ try {
+ m_pImpl->ReadContents();
+ }
+ catch ( const uno::RuntimeException& aRuntimeException )
+ {
+ SAL_INFO("package.xstor", "Rethrow: " << aRuntimeException.Message);
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ getXWeak(),
+ aCaught );
+ }
+
+ uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW );
+ try
+ {
+ xPackPropSet->setPropertyValue( ENCRYPTION_GPG_PROPERTIES,
+ uno::Any( aProps ) );
+ }
+ catch ( const uno::RuntimeException& aRuntimeException )
+ {
+ SAL_INFO("package.xstor", "Rethrow: " << aRuntimeException.Message);
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ getXWeak(),
+ aCaught );
+ }
+}
+
+uno::Sequence< beans::NamedValue > SAL_CALL OStorage::getEncryptionAlgorithms()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
+
+ uno::Sequence< beans::NamedValue > aResult;
+ SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "getEncryptionAlgorithms() method is not available for nonroot storages!" );
+ if ( m_pImpl->m_bIsRoot )
+ {
+ try {
+ m_pImpl->ReadContents();
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ getXWeak(),
+ aCaught );
+ }
+
+ uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW );
+ try
+ {
+ xPackPropSet->getPropertyValue( ENCRYPTION_ALGORITHMS_PROPERTY ) >>= aResult;
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ getXWeak(),
+ aCaught );
+ }
+ }
+
+ return aResult;
+}
+
+// XPropertySet
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL OStorage::getPropertySetInfo()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ //TODO:
+ return uno::Reference< beans::XPropertySetInfo >();
+}
+
+void SAL_CALL OStorage::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ //TODO: think about interaction handler
+
+ // WORKAROUND:
+ // The old document might have no version in the manifest.xml, so we have to allow to set the version
+ // even for readonly storages, so that the version from content.xml can be used.
+ if ( m_bReadOnlyWrap && aPropertyName != "Version" )
+ throw uno::RuntimeException( THROW_WHERE ); // TODO: Access denied
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::ZIP )
+ throw beans::UnknownPropertyException( aPropertyName );
+ else if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE )
+ {
+ if ( aPropertyName == "MediaType" )
+ {
+ aValue >>= m_pImpl->m_aMediaType;
+ m_pImpl->m_bControlMediaType = true;
+
+ m_pImpl->m_bBroadcastModified = true;
+ m_pImpl->m_bIsModified = true;
+ }
+ else if ( aPropertyName == "Version" )
+ {
+ aValue >>= m_pImpl->m_aVersion;
+ m_pImpl->m_bControlVersion = true;
+
+ // this property can be set even for readonly storage
+ if ( !m_bReadOnlyWrap )
+ {
+ m_pImpl->m_bBroadcastModified = true;
+ m_pImpl->m_bIsModified = true;
+ }
+ }
+ else if ( ( m_pImpl->m_bIsRoot && ( aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY
+ || aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY
+ || aPropertyName == IS_INCONSISTENT_PROPERTY
+ || aPropertyName == "URL"
+ || aPropertyName == "RepairPackage"
+ || aPropertyName == ENCRYPTION_GPG_PROPERTIES) )
+ || aPropertyName == "IsRoot"
+ || aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY )
+ throw beans::PropertyVetoException( THROW_WHERE );
+ else
+ throw beans::UnknownPropertyException( aPropertyName );
+ }
+ else if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML )
+ {
+ if ( aPropertyName == "RelationsInfoStream" )
+ {
+ uno::Reference< io::XInputStream > xInRelStream;
+ if ( !( aValue >>= xInRelStream ) || !xInRelStream.is() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ uno::Reference< io::XSeekable > xSeek( xInRelStream, uno::UNO_QUERY );
+ if ( !xSeek.is() )
+ {
+ // currently this is an internal property that is used for optimization
+ // and the stream must support XSeekable interface
+ // TODO/LATER: in future it can be changed if property is used from outside
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+ }
+
+ m_pImpl->m_xNewRelInfoStream = xInRelStream;
+ m_pImpl->m_aRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED_STREAM;
+ m_pImpl->m_bBroadcastModified = true;
+ m_pImpl->m_bIsModified = true;
+ }
+ else if ( aPropertyName == "RelationsInfo" )
+ {
+ if ( !(aValue >>= m_pImpl->m_aRelInfo) )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ m_pImpl->m_xNewRelInfoStream.clear();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED;
+ m_pImpl->m_bBroadcastModified = true;
+ m_pImpl->m_bIsModified = true;
+ }
+ else if ( ( m_pImpl->m_bIsRoot && ( aPropertyName == "URL" || aPropertyName == "RepairPackage") )
+ || aPropertyName == "IsRoot" )
+ throw beans::PropertyVetoException( THROW_WHERE );
+ else
+ throw beans::UnknownPropertyException( aPropertyName );
+ }
+ else
+ throw beans::UnknownPropertyException( aPropertyName );
+
+ BroadcastModifiedIfNecessary();
+}
+
+uno::Any SAL_CALL OStorage::getPropertyValue( const OUString& aPropertyName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE
+ && ( aPropertyName == "MediaType" || aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY || aPropertyName == "Version" ) )
+ {
+ try
+ {
+ m_pImpl->ReadContents();
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetException(
+ "Can't read contents!",
+ getXWeak(),
+ aCaught );
+ }
+
+ if ( aPropertyName == "MediaType" )
+ return uno::Any( m_pImpl->m_aMediaType );
+ else if ( aPropertyName == "Version" )
+ return uno::Any( m_pImpl->m_aVersion );
+ else
+ return uno::Any( m_pImpl->m_bMTFallbackUsed );
+ }
+ else if ( aPropertyName == "IsRoot" )
+ {
+ return uno::Any( m_pImpl->m_bIsRoot );
+ }
+ else if ( aPropertyName == "OpenMode" )
+ {
+ return uno::Any( m_pImpl->m_nStorageMode );
+ }
+ else if ( m_pImpl->m_bIsRoot )
+ {
+ if ( aPropertyName == "URL"
+ || aPropertyName == "RepairPackage" )
+ {
+ auto pProp = std::find_if(std::cbegin(m_pImpl->m_xProperties), std::cend(m_pImpl->m_xProperties),
+ [&aPropertyName](const css::beans::PropertyValue& rProp) { return rProp.Name == aPropertyName; });
+ if (pProp != std::cend(m_pImpl->m_xProperties))
+ return pProp->Value;
+
+ if ( aPropertyName == "URL" )
+ return uno::Any( OUString() );
+
+ return uno::Any( false ); // RepairPackage
+ }
+ else if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE
+ && ( aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY
+ || aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY
+ || aPropertyName == ENCRYPTION_GPG_PROPERTIES
+ || aPropertyName == IS_INCONSISTENT_PROPERTY ) )
+ {
+ try {
+ m_pImpl->ReadContents();
+ uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW );
+ return xPackPropSet->getPropertyValue( aPropertyName );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw lang::WrappedTargetException( THROW_WHERE "Can not open package!",
+ getXWeak(),
+ aCaught );
+ }
+ }
+ }
+
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+void SAL_CALL OStorage::addPropertyChangeListener(
+ const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ //TODO:
+}
+
+void SAL_CALL OStorage::removePropertyChangeListener(
+ const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ //TODO:
+}
+
+void SAL_CALL OStorage::addVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ //TODO:
+}
+
+void SAL_CALL OStorage::removeVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ //TODO:
+}
+
+// XRelationshipAccess
+
+// TODO/LATER: the storage and stream implementations of this interface are very similar, they could use a helper class
+
+sal_Bool SAL_CALL OStorage::hasByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ try
+ {
+ getRelationshipByID( sID );
+ return true;
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ }
+
+ return false;
+}
+
+namespace
+{
+
+const beans::StringPair* lcl_findPairByName(const uno::Sequence<beans::StringPair>& rSeq, const OUString& rName)
+{
+ return std::find_if(rSeq.begin(), rSeq.end(), [&rName](const beans::StringPair& rPair) { return rPair.First == rName; });
+}
+
+}
+
+OUString SAL_CALL OStorage::getTargetByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID );
+ auto pRel = lcl_findPairByName(aSeq, "Target");
+ if (pRel != aSeq.end())
+ return pRel->Second;
+
+ return OUString();
+}
+
+OUString SAL_CALL OStorage::getTypeByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID );
+ auto pRel = lcl_findPairByName(aSeq, "Type");
+ if (pRel != aSeq.end())
+ return pRel->Second;
+
+ return OUString();
+}
+
+uno::Sequence< beans::StringPair > SAL_CALL OStorage::getRelationshipByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ const beans::StringPair aIDRel("Id", sID);
+
+ auto pRel = std::find_if(aSeq.begin(), aSeq.end(),
+ [&aIDRel](const uno::Sequence<beans::StringPair>& rRel) {
+ return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); });
+ if (pRel != aSeq.end())
+ return *pRel;
+
+ throw container::NoSuchElementException( THROW_WHERE );
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OStorage::getRelationshipsByType( const OUString& sType )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ std::vector< uno::Sequence< beans::StringPair > > aResult;
+ aResult.reserve(aSeq.getLength());
+
+ std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResult),
+ [&sType](const uno::Sequence<beans::StringPair>& rRel) {
+ auto pRel = lcl_findPairByName(rRel, "Type");
+ return pRel != rRel.end()
+ // the type is usually a URL, so the check should be case insensitive
+ && pRel->Second.equalsIgnoreAsciiCase( sType );
+ });
+
+ return comphelper::containerToSequence(aResult);
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OStorage::getAllRelationships()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ uno::Sequence< uno::Sequence< beans::StringPair > > aRet;
+ try
+ {
+ aRet = m_pImpl->GetAllRelationshipsIfAny();
+ }
+ catch (const io::IOException&)
+ {
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception &)
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(THROW_WHERE "Can't getAllRelationships!",
+ uno::Reference< uno::XInterface >(),
+ aCaught);
+ }
+
+ return aRet;
+}
+
+void SAL_CALL OStorage::insertRelationshipByID( const OUString& sID, const uno::Sequence< beans::StringPair >& aEntry, sal_Bool bReplace )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ const beans::StringPair aIDRel("Id", sID);
+
+ uno::Sequence<beans::StringPair>* pResult = nullptr;
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ for ( sal_Int32 nInd = 0; nInd < aSeq.getLength(); nInd++ )
+ {
+ const auto& rRel = aSeq[nInd];
+ if (std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end())
+ pResult = &aSeq.getArray()[nInd];
+ }
+
+ if ( pResult && !bReplace )
+ throw container::ElementExistException( THROW_WHERE );
+
+ if ( !pResult )
+ {
+ const sal_Int32 nIDInd = aSeq.getLength();
+ aSeq.realloc( nIDInd + 1 );
+ pResult = &aSeq.getArray()[nIDInd];
+ }
+
+ std::vector<beans::StringPair> aResult;
+ aResult.reserve(aEntry.getLength() + 1);
+
+ aResult.push_back(aIDRel);
+ std::copy_if(aEntry.begin(), aEntry.end(), std::back_inserter(aResult),
+ [](const beans::StringPair& rPair) { return rPair.First != "Id"; });
+
+ *pResult = comphelper::containerToSequence(aResult);
+
+ m_pImpl->m_aRelInfo = aSeq;
+ m_pImpl->m_xNewRelInfoStream.clear();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED;
+}
+
+void SAL_CALL OStorage::removeRelationshipByID( const OUString& sID )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ const beans::StringPair aIDRel("Id", sID);
+ auto pRel = std::find_if(std::cbegin(aSeq), std::cend(aSeq),
+ [&aIDRel](const uno::Sequence< beans::StringPair >& rRel) {
+ return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); });
+ if (pRel != std::cend(aSeq))
+ {
+ auto nInd = static_cast<sal_Int32>(std::distance(std::cbegin(aSeq), pRel));
+ comphelper::removeElementAt(aSeq, nInd);
+
+ m_pImpl->m_aRelInfo = aSeq;
+ m_pImpl->m_xNewRelInfoStream.clear();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED;
+
+ // TODO/LATER: in future the unification of the ID could be checked
+ return;
+ }
+
+ throw container::NoSuchElementException( THROW_WHERE );
+}
+
+void SAL_CALL OStorage::insertRelationships( const uno::Sequence< uno::Sequence< beans::StringPair > >& aEntries, sal_Bool bReplace )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ OUString aIDTag( "Id" );
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
+ std::vector< uno::Sequence<beans::StringPair> > aResultVec;
+ aResultVec.reserve(aSeq.getLength() + aEntries.getLength());
+
+ std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResultVec),
+ [&aIDTag, &aEntries, bReplace](const uno::Sequence<beans::StringPair>& rTargetRel) {
+ auto pTargetPair = lcl_findPairByName(rTargetRel, aIDTag);
+ if (pTargetPair == rTargetRel.end())
+ return false;
+
+ bool bIsSourceSame = std::any_of(aEntries.begin(), aEntries.end(),
+ [&pTargetPair](const uno::Sequence<beans::StringPair>& rSourceEntry) {
+ return std::find(rSourceEntry.begin(), rSourceEntry.end(), *pTargetPair) != rSourceEntry.end(); });
+
+ if ( bIsSourceSame && !bReplace )
+ throw container::ElementExistException( THROW_WHERE );
+
+ // if no such element in the provided sequence
+ return !bIsSourceSame;
+ });
+
+ std::transform(aEntries.begin(), aEntries.end(), std::back_inserter(aResultVec),
+ [&aIDTag](const uno::Sequence<beans::StringPair>& rEntry) -> uno::Sequence<beans::StringPair> {
+ auto pPair = lcl_findPairByName(rEntry, aIDTag);
+ if (pPair == rEntry.end())
+ throw io::IOException( THROW_WHERE ); // TODO: illegal relation ( no ID )
+
+ auto aResult = comphelper::sequenceToContainer<std::vector<beans::StringPair>>(rEntry);
+ auto nIDInd = std::distance(rEntry.begin(), pPair);
+ std::rotate(aResult.begin(), std::next(aResult.begin(), nIDInd), std::next(aResult.begin(), nIDInd + 1));
+
+ return comphelper::containerToSequence(aResult);
+ });
+
+ m_pImpl->m_aRelInfo = comphelper::containerToSequence(aResultVec);
+ m_pImpl->m_xNewRelInfoStream.clear();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED;
+}
+
+void SAL_CALL OStorage::clearRelationships()
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ m_pImpl->m_aRelInfo.realloc( 0 );
+ m_pImpl->m_xNewRelInfoStream.clear();
+ m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED;
+}
+
+// XOptimizedStorage
+void SAL_CALL OStorage::insertRawNonEncrStreamElementDirect(
+ const OUString& /*sStreamName*/,
+ const uno::Reference< io::XInputStream >& /*xInStream*/ )
+{
+ // not implemented currently because there is still no demand
+ // might need to be implemented if direct copying of compressed streams is used
+ throw io::IOException( THROW_WHERE );
+}
+
+void SAL_CALL OStorage::insertStreamElementDirect(
+ const OUString& aStreamName,
+ const uno::Reference< io::XInputStream >& xInStream,
+ const uno::Sequence< beans::PropertyValue >& aProps )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStreamName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable storage name
+
+ if ( m_bReadOnlyWrap )
+ throw io::IOException( THROW_WHERE ); // TODO: access denied
+
+ try
+ {
+ SotElement_Impl* pElement = m_pImpl->FindElement( aStreamName );
+
+ if ( pElement )
+ throw container::ElementExistException( THROW_WHERE );
+
+ pElement = OpenStreamElement_Impl( aStreamName, embed::ElementModes::READWRITE, false );
+ OSL_ENSURE(pElement && pElement->m_xStream, "In case element can not be created an exception must be thrown!");
+
+ pElement->m_xStream->InsertStreamDirectly(xInStream, aProps);
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const container::ElementExistException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't insert stream directly!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+void SAL_CALL OStorage::copyElementDirectlyTo(
+ const OUString& aElementName,
+ const uno::Reference< embed::XOptimizedStorage >& xDest,
+ const OUString& aNewName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false )
+ || aNewName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aNewName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( !xDest.is() || xDest == getXWeak() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && ( aElementName == "_rels" || aNewName == "_rels" ) )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 ); // unacceptable name
+
+ try
+ {
+ SotElement_Impl* pElement = m_pImpl->FindElement( aElementName );
+ if ( !pElement )
+ throw container::NoSuchElementException( THROW_WHERE );
+
+ uno::Reference< XNameAccess > xNameAccess( xDest, uno::UNO_QUERY_THROW );
+ if ( xNameAccess->hasByName( aNewName ) )
+ throw container::ElementExistException( THROW_WHERE );
+
+ // let the element be copied directly
+ uno::Reference< embed::XStorage > xStorDest( xDest, uno::UNO_QUERY_THROW );
+ m_pImpl->CopyStorageElement( pElement, xStorDest, aNewName, true );
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const container::ElementExistException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't copy element directly!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+void SAL_CALL OStorage::writeAndAttachToStream( const uno::Reference< io::XStream >& xStream )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( !m_pImpl->m_bIsRoot )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ if ( !m_pImpl->m_pSwitchStream )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ try
+ {
+ m_pImpl->m_pSwitchStream->CopyAndSwitchPersistenceTo( xStream );
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:" );
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't write and attach to stream!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+}
+
+void SAL_CALL OStorage::attachToURL( const OUString& sURL,
+ sal_Bool bReadOnly )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( !m_pImpl->m_bIsRoot )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ if ( !m_pImpl->m_pSwitchStream )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ uno::Reference < ucb::XSimpleFileAccess3 > xAccess(
+ ucb::SimpleFileAccess::create( m_pImpl->m_xContext ) );
+
+ try
+ {
+ if ( bReadOnly )
+ {
+ uno::Reference< io::XInputStream > xInputStream = xAccess->openFileRead( sURL );
+ m_pImpl->m_pSwitchStream->SwitchPersistenceTo( xInputStream );
+ }
+ else
+ {
+ uno::Reference< io::XStream > xStream = xAccess->openFileReadWrite( sURL );
+ m_pImpl->m_pSwitchStream->SwitchPersistenceTo( xStream );
+ }
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't attach to URL!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+uno::Any SAL_CALL OStorage::getElementPropertyValue( const OUString& aElementName, const OUString& aPropertyName )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aElementName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // TODO: unacceptable name
+
+ try
+ {
+ SotElement_Impl *pElement = m_pImpl->FindElement( aElementName );
+ if ( !pElement )
+ throw container::NoSuchElementException( THROW_WHERE );
+
+ // TODO/LATER: Currently it is only implemented for MediaType property of substorages, might be changed in future
+ if ( !pElement->m_bIsStorage || m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE || aPropertyName != "MediaType" )
+ throw beans::PropertyVetoException( THROW_WHERE );
+
+ if (!pElement->m_xStorage)
+ m_pImpl->OpenSubStorage( pElement, embed::ElementModes::READ );
+
+ if (!pElement->m_xStorage)
+ throw io::IOException( THROW_WHERE ); // TODO: general_error
+
+ pElement->m_xStorage->ReadContents();
+ return uno::Any(pElement->m_xStorage->m_aMediaType);
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const container::NoSuchElementException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const beans::PropertyVetoException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't get element property!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+}
+
+void SAL_CALL OStorage::copyStreamElementData( const OUString& aStreamName, const uno::Reference< io::XStream >& xTargetStream )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStreamName == "_rels" )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable name
+
+ if ( !xTargetStream.is() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
+
+ try
+ {
+ uno::Reference< io::XStream > xNonconstRef = xTargetStream;
+ m_pImpl->CloneStreamElement( aStreamName, false, ::comphelper::SequenceAsHashMap(), xNonconstRef );
+
+ SAL_WARN_IF( xNonconstRef != xTargetStream, "package.xstor", "The provided stream reference seems not be filled in correctly!" );
+ if ( xNonconstRef != xTargetStream )
+ throw uno::RuntimeException( THROW_WHERE ); // if the stream reference is set it must not be changed!
+ }
+ catch( const embed::InvalidStorageException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const embed::StorageWrappedTargetException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:");
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
+
+ throw embed::StorageWrappedTargetException( THROW_WHERE "Can't copy stream data!",
+ uno::Reference< io::XInputStream >(),
+ aCaught );
+ }
+
+}
+
+// XHierarchicalStorageAccess
+uno::Reference< embed::XExtendedStorageStream > SAL_CALL OStorage::openStreamElementByHierarchicalName( const OUString& aStreamPath, ::sal_Int32 nOpenMode )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aStreamPath.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamPath, true ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE )
+ && ( nOpenMode & embed::ElementModes::WRITE ) )
+ throw io::IOException( THROW_WHERE ); // Access denied
+
+ std::vector<OUString> aListPath = OHierarchyHolder_Impl::GetListPathFromString( aStreamPath );
+ OSL_ENSURE( aListPath.size(), "The result list must not be empty!" );
+
+ uno::Reference< embed::XExtendedStorageStream > xResult;
+ if ( aListPath.size() == 1 )
+ {
+ try
+ {
+ // that must be a direct request for a stream
+ // the transacted version of the stream should be opened
+
+ SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamPath, nOpenMode, false );
+ assert(pElement && pElement->m_xStream && "In case element can not be created an exception must be thrown!");
+
+ xResult.set(pElement->m_xStream->GetStream(nOpenMode, true),
+ uno::UNO_QUERY_THROW);
+ }
+ catch ( const container::NoSuchElementException & )
+ {
+ throw io::IOException( THROW_WHERE ); // file not found
+ }
+ }
+ else
+ {
+ // there are still storages in between
+ if ( !m_rHierarchyHolder.is() )
+ m_rHierarchyHolder = new OHierarchyHolder_Impl(
+ uno::Reference< embed::XStorage >( static_cast< embed::XStorage* >( this ) ) );
+
+ xResult = m_rHierarchyHolder->GetStreamHierarchically(
+ ( m_pImpl->m_nStorageMode & embed::ElementModes::READWRITE ),
+ aListPath,
+ nOpenMode );
+ }
+
+ if ( !xResult.is() )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ return xResult;
+}
+
+uno::Reference< embed::XExtendedStorageStream > SAL_CALL OStorage::openEncryptedStreamElementByHierarchicalName( const OUString& aStreamPath, ::sal_Int32 nOpenMode, const OUString& sPassword )
+{
+ return openEncryptedStreamByHierarchicalName( aStreamPath, nOpenMode, ::comphelper::OStorageHelper::CreatePackageEncryptionData( sPassword ) );
+}
+
+void SAL_CALL OStorage::removeStreamElementByHierarchicalName( const OUString& aStreamPath )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( aStreamPath.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamPath, true ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE ) )
+ throw io::IOException( THROW_WHERE ); // Access denied
+
+ std::vector<OUString> aListPath = OHierarchyHolder_Impl::GetListPathFromString( aStreamPath );
+ OSL_ENSURE( aListPath.size(), "The result list must not be empty!" );
+
+ if ( !m_rHierarchyHolder.is() )
+ m_rHierarchyHolder = new OHierarchyHolder_Impl(
+ uno::Reference< embed::XStorage >( static_cast< embed::XStorage* >( this ) ) );
+
+ m_rHierarchyHolder->RemoveStreamHierarchically( aListPath );
+}
+
+// XHierarchicalStorageAccess2
+uno::Reference< embed::XExtendedStorageStream > SAL_CALL OStorage::openEncryptedStreamByHierarchicalName( const OUString& aStreamPath, ::sal_Int32 nOpenMode, const uno::Sequence< beans::NamedValue >& aEncryptionData )
+{
+ ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw packages::NoEncryptionException( THROW_WHERE );
+
+ if ( aStreamPath.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamPath, true ) )
+ throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 );
+
+ if ( !aEncryptionData.hasElements() )
+ throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 3 );
+
+ if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE )
+ && ( nOpenMode & embed::ElementModes::WRITE ) )
+ throw io::IOException( THROW_WHERE ); // Access denied
+
+ std::vector<OUString> aListPath = OHierarchyHolder_Impl::GetListPathFromString( aStreamPath );
+ OSL_ENSURE( aListPath.size(), "The result list must not be empty!" );
+
+ uno::Reference< embed::XExtendedStorageStream > xResult;
+ if ( aListPath.size() == 1 )
+ {
+ // that must be a direct request for a stream
+ // the transacted version of the stream should be opened
+
+ SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamPath, nOpenMode, true );
+ OSL_ENSURE(pElement && pElement->m_xStream, "In case element can not be created an exception must be thrown!");
+
+ xResult.set(pElement->m_xStream->GetStream(nOpenMode, aEncryptionData, true),
+ uno::UNO_QUERY_THROW);
+ }
+ else
+ {
+ // there are still storages in between
+ if ( !m_rHierarchyHolder.is() )
+ m_rHierarchyHolder = new OHierarchyHolder_Impl(
+ uno::Reference< embed::XStorage >( static_cast< embed::XStorage* >( this ) ) );
+
+ xResult = m_rHierarchyHolder->GetStreamHierarchically(
+ ( m_pImpl->m_nStorageMode & embed::ElementModes::READWRITE ),
+ aListPath,
+ nOpenMode,
+ aEncryptionData );
+ }
+
+ if ( !xResult.is() )
+ throw uno::RuntimeException( THROW_WHERE );
+
+ return xResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/xstor/xstorage.hxx b/package/source/xstor/xstorage.hxx
new file mode 100644
index 0000000000..54fe49a9d9
--- /dev/null
+++ b/package/source/xstor/xstorage.hxx
@@ -0,0 +1,540 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_XSTORAGE_HXX
+#define INCLUDED_PACKAGE_SOURCE_XSTOR_XSTORAGE_HXX
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/embed/XStorage2.hpp>
+#include <com/sun/star/embed/XOptimizedStorage.hpp>
+#include <com/sun/star/embed/XHierarchicalStorageAccess2.hpp>
+#include <com/sun/star/embed/XStorageRawAccess.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/XTransactionBroadcaster.hpp>
+#include <com/sun/star/embed/XEncryptionProtectedStorage.hpp>
+#include <com/sun/star/embed/XRelationshipAccess.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <comphelper/multicontainer2.hxx>
+#include <comphelper/refcountedmutex.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <o3tl/deleter.hxx>
+#include <rtl/ref.hxx>
+
+#include "ohierarchyholder.hxx"
+#include "disposelistener.hxx"
+
+#include <vector>
+#include <memory>
+#include <optional>
+#include <string_view>
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+#define RELINFO_NO_INIT 1
+#define RELINFO_READ 2
+#define RELINFO_CHANGED 3
+#define RELINFO_CHANGED_STREAM 4
+#define RELINFO_CHANGED_STREAM_READ 5
+#define RELINFO_BROKEN 6
+#define RELINFO_CHANGED_BROKEN 7
+
+#define STOR_MESS_PRECOMMIT 1
+#define STOR_MESS_COMMITTED 2
+#define STOR_MESS_PREREVERT 3
+#define STOR_MESS_REVERTED 4
+
+// a common implementation for an entry
+
+struct OStorage_Impl;
+struct OWriteStream_Impl;
+
+struct SotElement_Impl
+{
+ OUString m_aOriginalName;
+ bool m_bIsRemoved;
+ bool m_bIsInserted;
+ bool m_bIsStorage;
+
+ std::unique_ptr<OStorage_Impl> m_xStorage;
+ std::unique_ptr<OWriteStream_Impl, o3tl::default_delete<OWriteStream_Impl>> m_xStream;
+
+public:
+ SotElement_Impl(OUString aName, bool bStor, bool bNew);
+};
+
+// Main storage implementation
+
+class OStorage;
+
+struct StorageHolder_Impl
+{
+ OStorage* m_pPointer;
+ css::uno::WeakReference< css::embed::XStorage > m_xWeakRef;
+
+ explicit inline StorageHolder_Impl( OStorage* pStorage );
+};
+
+class SwitchablePersistenceStream;
+struct OStorage_Impl
+{
+ typedef std::vector<StorageHolder_Impl> StorageHoldersType;
+
+ rtl::Reference<comphelper::RefCountedMutex> m_xMutex;
+
+ OStorage* m_pAntiImpl; // only valid if external references exists
+ StorageHoldersType m_aReadOnlyWrapVector; // only valid if readonly external reference exists
+
+ sal_Int32 m_nStorageMode; // open mode ( read/write/trunc/nocreate )
+ bool m_bIsModified; // only modified elements will be sent to the original content
+ bool m_bBroadcastModified; // will be set if notification is required
+
+ bool m_bCommited; // sending the streams is coordinated by the root storage of the package
+
+ bool m_bIsRoot; // marks this storage as root storages that manages all commits and reverts
+ bool m_bListCreated;
+ bool m_bRepairPackage = false;
+
+ /// Count of registered modification listeners
+ oslInterlockedCount m_nModifiedListenerCount;
+ bool HasModifiedListener() const
+ {
+ return m_nModifiedListenerCount > 0 && m_pAntiImpl != nullptr;
+ }
+
+ std::unordered_map<OUString, std::vector<SotElement_Impl*>> m_aChildrenMap;
+ std::vector< SotElement_Impl* > m_aDeletedVector;
+
+ css::uno::Reference< css::container::XNameContainer > m_xPackageFolder;
+
+ css::uno::Reference< css::lang::XSingleServiceFactory > m_xPackage;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ // valid only for root storage
+ css::uno::Reference< css::io::XInputStream > m_xInputStream; // ??? may be stored in properties
+ css::uno::Reference< css::io::XStream > m_xStream; // ??? may be stored in properties
+ css::uno::Sequence< css::beans::PropertyValue > m_xProperties;
+ bool m_bHasCommonEncryptionData;
+ ::comphelper::SequenceAsHashMap m_aCommonEncryptionData;
+
+ // must be empty in case of root storage
+ OStorage_Impl* m_pParent;
+
+ bool m_bControlMediaType;
+ OUString m_aMediaType;
+ bool m_bMTFallbackUsed;
+
+ bool m_bControlVersion;
+ OUString m_aVersion;
+
+ rtl::Reference<SwitchablePersistenceStream> m_pSwitchStream;
+
+ sal_Int32 m_nStorageType; // the mode in which the storage is used
+
+ // the _rels substorage that is handled in a special way in embed::StorageFormats::OFOPXML
+ SotElement_Impl* m_pRelStorElement;
+ css::uno::Reference< css::embed::XStorage > m_xRelStorage;
+ css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aRelInfo;
+ css::uno::Reference< css::io::XInputStream > m_xNewRelInfoStream;
+ sal_Int16 m_nRelInfoStatus;
+
+ // Constructors
+ OStorage_Impl( css::uno::Reference< css::io::XInputStream > const & xInputStream,
+ sal_Int32 nMode,
+ const css::uno::Sequence< css::beans::PropertyValue >& xProperties,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType );
+
+ OStorage_Impl( css::uno::Reference< css::io::XStream > const & xStream,
+ sal_Int32 nMode,
+ const css::uno::Sequence< css::beans::PropertyValue >& xProperties,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType );
+
+ // constructor for a substorage
+ OStorage_Impl( OStorage_Impl* pParent,
+ sal_Int32 nMode,
+ css::uno::Reference< css::container::XNameContainer > const & xPackageFolder,
+ css::uno::Reference< css::lang::XSingleServiceFactory > xPackage,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType );
+
+ ~OStorage_Impl();
+
+ void SetReadOnlyWrap( OStorage& aStorage );
+ void RemoveReadOnlyWrap( const OStorage& aStorage );
+
+ void OpenOwnPackage();
+ void ReadContents();
+ void ReadRelInfoIfNecessary();
+
+ bool HasChildren();
+ void GetStorageProperties();
+
+ css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > GetAllRelationshipsIfAny();
+ void CopyLastCommitTo( const css::uno::Reference< css::embed::XStorage >& xNewStor );
+
+ void InsertIntoPackageFolder(
+ const OUString& aName,
+ const css::uno::Reference< css::container::XNameContainer >& xParentPackageFolder );
+
+ void Commit();
+ void Revert();
+
+ /// @throws css::packages::NoEncryptionException
+ ::comphelper::SequenceAsHashMap GetCommonRootEncryptionData();
+
+ void CopyToStorage( const css::uno::Reference< css::embed::XStorage >& xDest,
+ bool bDirect );
+ void CopyStorageElement( SotElement_Impl* pElement,
+ const css::uno::Reference< css::embed::XStorage >& xDest,
+ const OUString& aName,
+ bool bDirect );
+
+ SotElement_Impl* FindElement( const OUString& rName );
+
+ SotElement_Impl* InsertStream( const OUString& aName, bool bEncr );
+ void InsertRawStream( const OUString& aName, const css::uno::Reference< css::io::XInputStream >& xInStream );
+
+ std::unique_ptr<OStorage_Impl> CreateNewStorageImpl( sal_Int32 nStorageMode );
+ SotElement_Impl* InsertStorage( const OUString& aName, sal_Int32 nStorageMode );
+ SotElement_Impl* InsertElement( const OUString& aName, bool bIsStorage );
+
+ void OpenSubStorage( SotElement_Impl* pElement, sal_Int32 nStorageMode );
+ void OpenSubStream( SotElement_Impl* pElement );
+
+ css::uno::Sequence< OUString > GetElementNames();
+
+ void RemoveElement( OUString const & rName, SotElement_Impl* pElement );
+ static void ClearElement( SotElement_Impl* pElement );
+
+ /// @throws css::embed::InvalidStorageException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::packages::WrongPasswordException
+ /// @throws css::packages::NoEncryptionException
+ /// @throws css::container::NoSuchElementException
+ /// @throws css::io::IOException
+ /// @throws css::embed::StorageWrappedTargetException
+ /// @throws css::uno::RuntimeException
+ void CloneStreamElement(
+ const OUString& aStreamName,
+ bool bPassProvided,
+ const ::comphelper::SequenceAsHashMap& aEncryptionData,
+ css::uno::Reference< css::io::XStream >& xTargetStream );
+
+ void RemoveStreamRelInfo( std::u16string_view aOriginalName );
+ void CreateRelStorage();
+ void CommitStreamRelInfo( std::u16string_view rName, SotElement_Impl const * pStreamElement );
+ css::uno::Reference< css::io::XInputStream > GetRelInfoStreamForName(
+ std::u16string_view aName );
+ void CommitRelInfo( const css::uno::Reference< css::container::XNameContainer >& xNewPackageFolder );
+
+ static void completeStorageStreamCopy_Impl(
+ const css::uno::Reference< css::io::XStream >& xSource,
+ const css::uno::Reference< css::io::XStream >& xDest,
+ sal_Int32 nStorageType,
+ const css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > >& aRelInfo );
+
+};
+
+class OStorage final : public css::lang::XTypeProvider
+ , public css::embed::XStorage2
+ , public css::embed::XStorageRawAccess
+ , public css::embed::XTransactedObject
+ , public css::embed::XTransactionBroadcaster
+ , public css::util::XModifiable
+ , public css::embed::XEncryptionProtectedStorage
+ , public css::beans::XPropertySet
+ , public css::embed::XOptimizedStorage
+ , public css::embed::XRelationshipAccess
+ , public css::embed::XHierarchicalStorageAccess2
+ , public ::cppu::OWeakObject
+{
+ OStorage_Impl* m_pImpl;
+ rtl::Reference<comphelper::RefCountedMutex> m_xSharedMutex;
+ comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenersContainer; // list of listeners
+ ::std::optional< ::cppu::OTypeCollection> m_oTypeCollection;
+ bool m_bReadOnlyWrap;
+ ::rtl::Reference<OChildDispListener_Impl> m_pSubElDispListener;
+ ::std::vector< css::uno::WeakReference< css::lang::XComponent > > m_aOpenSubComponentsVector;
+ ::rtl::Reference< OHierarchyHolder_Impl > m_rHierarchyHolder;
+
+ SotElement_Impl* OpenStreamElement_Impl( const OUString& aStreamName, sal_Int32 nOpenMode, bool bEncr );
+
+ void BroadcastModifiedIfNecessary();
+
+ void BroadcastTransaction( sal_Int8 nMessage );
+
+ void MakeLinkToSubComponent_Impl(
+ const css::uno::Reference< css::lang::XComponent >& xComponent );
+
+public:
+
+ OStorage( css::uno::Reference< css::io::XInputStream > const & xInputStream,
+ sal_Int32 nMode,
+ const css::uno::Sequence< css::beans::PropertyValue >& xProperties,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType );
+
+ OStorage( css::uno::Reference< css::io::XStream > const & xStream,
+ sal_Int32 nMode,
+ const css::uno::Sequence< css::beans::PropertyValue >& xProperties,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext,
+ sal_Int32 nStorageType );
+
+ OStorage( OStorage_Impl* pImpl, bool bReadOnlyWrap );
+
+ virtual ~OStorage() override;
+
+ void InternalDispose( bool bNotifyImpl );
+
+ void ChildIsDisposed( const css::uno::Reference< css::uno::XInterface >& xChild );
+
+ sal_Int32 GetRefCount_Impl() const { return m_refCount; }
+
+ // XInterface
+
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override;
+
+ virtual void SAL_CALL acquire() noexcept override;
+
+ virtual void SAL_CALL release() noexcept override;
+
+ // XTypeProvider
+
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+ // XStorage
+
+ virtual void SAL_CALL copyToStorage( const css::uno::Reference< css::embed::XStorage >& xDest ) override;
+
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL openStreamElement(
+ const OUString& aStreamName, sal_Int32 nOpenMode ) override;
+
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL openEncryptedStreamElement(
+ const OUString& aStreamName, sal_Int32 nOpenMode, const OUString& aPass ) override;
+
+ virtual css::uno::Reference< css::embed::XStorage > SAL_CALL openStorageElement(
+ const OUString& aStorName, sal_Int32 nStorageMode ) override;
+
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL cloneStreamElement(
+ const OUString& aStreamName ) override;
+
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL cloneEncryptedStreamElement(
+ const OUString& aStreamName, const OUString& aPass ) override;
+
+ virtual void SAL_CALL copyLastCommitTo(
+ const css::uno::Reference< css::embed::XStorage >& xTargetStorage ) override;
+
+ virtual void SAL_CALL copyStorageElementLastCommitTo(
+ const OUString& aStorName,
+ const css::uno::Reference< css::embed::XStorage >& xTargetStorage ) override;
+
+ virtual sal_Bool SAL_CALL isStreamElement( const OUString& aElementName ) override;
+
+ virtual sal_Bool SAL_CALL isStorageElement( const OUString& aElementName ) override;
+
+ virtual void SAL_CALL removeElement( const OUString& aElementName ) override;
+
+ virtual void SAL_CALL renameElement( const OUString& rEleName, const OUString& rNewName ) override;
+
+ virtual void SAL_CALL copyElementTo( const OUString& aElementName,
+ const css::uno::Reference< css::embed::XStorage >& xDest,
+ const OUString& aNewName ) override;
+
+ virtual void SAL_CALL moveElementTo( const OUString& aElementName,
+ const css::uno::Reference< css::embed::XStorage >& xDest,
+ const OUString& rNewName ) override;
+
+ // XStorage2
+
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL openEncryptedStream( const OUString& sStreamName, ::sal_Int32 nOpenMode, const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override;
+
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL cloneEncryptedStream( const OUString& sStreamName, const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override;
+
+ // XStorageRawAccess
+
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getPlainRawStreamElement(
+ const OUString& sStreamName ) override;
+
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getRawEncrStreamElement(
+ const OUString& sStreamName ) override;
+
+ virtual void SAL_CALL insertRawEncrStreamElement( const OUString& aStreamName,
+ const css::uno::Reference< css::io::XInputStream >& xInStream ) override;
+
+ // XTransactedObject
+ virtual void SAL_CALL commit() override;
+
+ virtual void SAL_CALL revert() override;
+
+ // XTransactionBroadcaster
+ virtual void SAL_CALL addTransactionListener(
+ const css::uno::Reference< css::embed::XTransactionListener >& aListener ) override;
+
+ virtual void SAL_CALL removeTransactionListener(
+ const css::uno::Reference< css::embed::XTransactionListener >& aListener ) override;
+
+ // XModifiable
+
+ virtual sal_Bool SAL_CALL isModified() override;
+
+ virtual void SAL_CALL setModified( sal_Bool bModified ) override;
+
+ virtual void SAL_CALL addModifyListener(
+ const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ virtual void SAL_CALL removeModifyListener(
+ const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ // XNameAccess
+
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override;
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ virtual css::uno::Type SAL_CALL getElementType() override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XComponent
+
+ virtual void SAL_CALL dispose() override;
+
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+
+ // XEncryptionProtectedSource
+
+ virtual void SAL_CALL setEncryptionPassword( const OUString& aPass ) override;
+
+ virtual void SAL_CALL removeEncryption() override;
+
+ // XEncryptionProtectedSource2
+
+ virtual void SAL_CALL setEncryptionData(
+ const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override;
+
+ virtual sal_Bool SAL_CALL hasEncryptionData() override;
+
+ // XEncryptionProtectedStorage
+
+ virtual void SAL_CALL setEncryptionAlgorithms( const css::uno::Sequence< css::beans::NamedValue >& aAlgorithms ) override;
+ virtual void SAL_CALL setGpgProperties( const css::uno::Sequence< css::uno::Sequence< css::beans::NamedValue > >& aCryptProps ) override;
+
+ virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL getEncryptionAlgorithms() override;
+
+ // XPropertySet
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& aPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& aPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& PropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ // XOptimizedStorage
+ virtual void SAL_CALL insertRawNonEncrStreamElementDirect( const OUString& sStreamName, const css::uno::Reference< css::io::XInputStream >& xInStream ) override;
+
+ virtual void SAL_CALL insertStreamElementDirect( const OUString& sStreamName, const css::uno::Reference< css::io::XInputStream >& xInStream, const css::uno::Sequence< css::beans::PropertyValue >& aProps ) override;
+
+ virtual void SAL_CALL copyElementDirectlyTo( const OUString& sSourceName, const css::uno::Reference< css::embed::XOptimizedStorage >& xTargetStorage, const OUString& sTargetName ) override;
+
+ virtual void SAL_CALL writeAndAttachToStream( const css::uno::Reference< css::io::XStream >& xStream ) override;
+
+ virtual void SAL_CALL attachToURL( const OUString& sURL, sal_Bool bReadOnly ) override;
+
+ virtual css::uno::Any SAL_CALL getElementPropertyValue( const OUString& sElementName, const OUString& sPropertyName ) override;
+
+ virtual void SAL_CALL copyStreamElementData( const OUString& sStreamName, const css::uno::Reference< css::io::XStream >& xTargetStream ) override;
+
+ // XRelationshipAccess
+ virtual sal_Bool SAL_CALL hasByID( const OUString& sID ) override;
+
+ virtual OUString SAL_CALL getTargetByID( const OUString& sID ) override;
+
+ virtual OUString SAL_CALL getTypeByID( const OUString& sID ) override;
+
+ virtual css::uno::Sequence< css::beans::StringPair > SAL_CALL getRelationshipByID( const OUString& sID ) override;
+
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getRelationshipsByType( const OUString& sType ) override;
+
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getAllRelationships( ) override;
+
+ virtual void SAL_CALL insertRelationshipByID( const OUString& sID, const css::uno::Sequence< css::beans::StringPair >& aEntry, sal_Bool bReplace ) override;
+
+ virtual void SAL_CALL removeRelationshipByID( const OUString& sID ) override;
+
+ virtual void SAL_CALL insertRelationships( const css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > >& aEntries, sal_Bool bReplace ) override;
+
+ virtual void SAL_CALL clearRelationships( ) override;
+
+ // XHierarchicalStorageAccess
+ virtual css::uno::Reference< css::embed::XExtendedStorageStream > SAL_CALL openStreamElementByHierarchicalName( const OUString& sStreamPath, ::sal_Int32 nOpenMode ) override;
+
+ virtual css::uno::Reference< css::embed::XExtendedStorageStream > SAL_CALL openEncryptedStreamElementByHierarchicalName( const OUString& sStreamName, ::sal_Int32 nOpenMode, const OUString& sPassword ) override;
+
+ virtual void SAL_CALL removeStreamElementByHierarchicalName( const OUString& sElementPath ) override;
+
+ // XHierarchicalStorageAccess2
+ virtual css::uno::Reference< css::embed::XExtendedStorageStream > SAL_CALL openEncryptedStreamByHierarchicalName( const OUString& sStreamName, ::sal_Int32 nOpenMode, const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override;
+};
+
+StorageHolder_Impl::StorageHolder_Impl( OStorage* pStorage )
+: m_pPointer( pStorage )
+, m_xWeakRef( css::uno::Reference< css::embed::XStorage >( pStorage ) )
+{
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ByteChucker.cxx b/package/source/zipapi/ByteChucker.cxx
new file mode 100644
index 0000000000..fe1f6a8685
--- /dev/null
+++ b/package/source/zipapi/ByteChucker.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ByteChucker.hxx>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+
+ByteChucker::ByteChucker(Reference<XOutputStream> const & xOstream)
+: xStream(xOstream)
+, xSeek (xOstream, UNO_QUERY )
+, a2Sequence ( 2 )
+, a4Sequence ( 4 )
+, a8Sequence ( 8 )
+, p2Sequence ( a2Sequence.getArray() )
+, p4Sequence ( a4Sequence.getArray() )
+, p8Sequence ( a8Sequence.getArray() )
+{
+}
+
+ByteChucker::~ByteChucker()
+{
+}
+
+void ByteChucker::WriteBytes( const Sequence< sal_Int8 >& aData )
+{
+ xStream->writeBytes(aData);
+}
+
+sal_Int64 ByteChucker::GetPosition( )
+{
+ return xSeek->getPosition();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ByteGrabber.cxx b/package/source/zipapi/ByteGrabber.cxx
new file mode 100644
index 0000000000..5a491de650
--- /dev/null
+++ b/package/source/zipapi/ByteGrabber.cxx
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ByteGrabber.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace ::com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+/** ByteGrabber implements the >> operators on an XOutputStream. This is
+ * potentially quite slow and may need to be optimised
+ */
+
+ByteGrabber::ByteGrabber(uno::Reference < io::XInputStream > const & xIstream)
+: xStream(xIstream)
+, xSeek (xIstream, uno::UNO_QUERY )
+, aSequence ( 4 )
+{
+ pSequence = aSequence.getArray();
+}
+
+ByteGrabber::~ByteGrabber()
+{
+}
+
+void ByteGrabber::setInputStream (const uno::Reference < io::XInputStream >& xNewStream)
+{
+ std::scoped_lock aGuard( m_aMutex );
+ xStream = xNewStream;
+ xSeek.set(xNewStream, uno::UNO_QUERY);
+}
+
+// XInputStream chained
+sal_Int32 ByteGrabber::readBytes( uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nBytesToRead )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ return xStream->readBytes(aData, nBytesToRead );
+}
+
+// XSeekable chained...
+void ByteGrabber::seek( sal_Int64 location )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!xSeek.is() )
+ throw io::IOException(THROW_WHERE );
+
+ xSeek->seek( location );
+}
+
+sal_Int64 ByteGrabber::getPosition( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!xSeek.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return xSeek->getPosition();
+}
+
+sal_Int64 ByteGrabber::getLength( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!xSeek.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return xSeek->getLength();
+}
+
+sal_uInt16 ByteGrabber::ReadUInt16()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if (xStream->readBytes(aSequence, 2) != 2)
+ return 0;
+
+ pSequence = aSequence.getConstArray();
+ return static_cast <sal_uInt16>
+ ( (pSequence[0] & 0xFF)
+ | (pSequence[1] & 0xFF) << 8);
+}
+
+sal_uInt32 ByteGrabber::ReadUInt32()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if (xStream->readBytes(aSequence, 4) != 4)
+ return 0;
+
+ pSequence = aSequence.getConstArray();
+ return static_cast < sal_uInt32 >
+ ( (pSequence[0] & 0xFF)
+ | ( pSequence[1] & 0xFF ) << 8
+ | ( pSequence[2] & 0xFF ) << 16
+ | ( pSequence[3] & 0xFF ) << 24 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/CRC32.cxx b/package/source/zipapi/CRC32.cxx
new file mode 100644
index 0000000000..bd06822486
--- /dev/null
+++ b/package/source/zipapi/CRC32.cxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <CRC32.hxx>
+#include <PackageConstants.hxx>
+#include <rtl/crc.h>
+#include <com/sun/star/io/XInputStream.hpp>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+
+/** A class to compute the CRC32 value of a data stream
+ */
+
+CRC32::CRC32()
+: nCRC(0)
+{
+}
+void CRC32::reset()
+{
+ nCRC=0;
+}
+sal_Int32 CRC32::getValue() const
+{
+ return nCRC;
+}
+/** Update CRC32 with specified sequence of bytes
+ */
+void CRC32::updateSegment(const Sequence< sal_Int8 > &b, sal_Int32 len)
+{
+ nCRC = rtl_crc32(nCRC, b.getConstArray(), len );
+}
+/** Update CRC32 with specified sequence of bytes
+ */
+void CRC32::update(const Sequence< sal_Int8 > &b)
+{
+ nCRC = rtl_crc32(nCRC, b.getConstArray(),b.getLength());
+}
+
+sal_Int64 CRC32::updateStream( Reference < XInputStream > const & xStream )
+{
+ sal_Int32 nLength;
+ sal_Int64 nTotal = 0;
+ Sequence < sal_Int8 > aSeq ( n_ConstBufferSize );
+ do
+ {
+ nLength = xStream->readBytes ( aSeq, n_ConstBufferSize );
+ updateSegment ( aSeq, nLength );
+ nTotal += nLength;
+ }
+ while ( nLength == n_ConstBufferSize );
+
+ return nTotal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/Deflater.cxx b/package/source/zipapi/Deflater.cxx
new file mode 100644
index 0000000000..9439e3f56b
--- /dev/null
+++ b/package/source/zipapi/Deflater.cxx
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <package/Deflater.hxx>
+#include <zlib.h>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <osl/diagnose.h>
+#include <string.h>
+
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star;
+using namespace ZipUtils;
+
+/** Provides general purpose compression using the ZLIB compression
+ * library.
+ */
+
+Deflater::~Deflater()
+{
+ end();
+}
+void Deflater::init (sal_Int32 nLevelArg, bool bNowrap)
+{
+ pStream.reset(new z_stream);
+ /* Memset it to 0...sets zalloc/zfree/opaque to NULL */
+ memset (pStream.get(), 0, sizeof(*pStream));
+
+ switch (deflateInit2(pStream.get(), nLevelArg, Z_DEFLATED, bNowrap? -MAX_WBITS : MAX_WBITS,
+ DEF_MEM_LEVEL, DEFAULT_STRATEGY))
+ {
+ case Z_OK:
+ break;
+ case Z_MEM_ERROR:
+ pStream.reset();
+ break;
+ case Z_STREAM_ERROR:
+ pStream.reset();
+ break;
+ default:
+ break;
+ }
+}
+
+Deflater::Deflater(sal_Int32 nSetLevel, bool bNowrap)
+: bFinish(false)
+, bFinished(false)
+, nOffset(0)
+, nLength(0)
+, nTotalOut64(0)
+, nTotalIn64(0)
+{
+ init(nSetLevel, bNowrap);
+}
+
+sal_Int32 Deflater::doDeflateBytes (uno::Sequence < sal_Int8 > &rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength)
+{
+ sal_Int32 nResult;
+ pStream->next_in = reinterpret_cast<const unsigned char*>( sInBuffer.getConstArray() + nOffset );
+ pStream->next_out = reinterpret_cast<unsigned char*>(rBuffer.getArray())+nNewOffset;
+ pStream->avail_in = nLength;
+ pStream->avail_out = nNewLength;
+ auto nLastTotalIn = pStream->total_in;
+ auto nLastTotalOut = pStream->total_out;
+
+#if !defined Z_PREFIX
+ nResult = deflate(pStream.get(), bFinish ? Z_FINISH : Z_NO_FLUSH);
+#else
+ nResult = z_deflate(pStream.get(), bFinish ? Z_FINISH : Z_NO_FLUSH);
+#endif
+ // total_in / total_out may stored only in 32bit, and can overflow during deflate
+ // 1 deflate call, uncompress only a few data, so only 1 overflow can happen at once.
+ if (pStream->total_in < nLastTotalIn)
+ {
+ nTotalIn64 += 0x100000000;
+ }
+ if (pStream->total_out < nLastTotalOut)
+ {
+ nTotalOut64 += 0x100000000;
+ }
+ switch (nResult)
+ {
+ case Z_STREAM_END:
+ bFinished = true;
+ [[fallthrough]];
+ case Z_OK:
+ nOffset += nLength - pStream->avail_in;
+ nLength = pStream->avail_in;
+ return nNewLength - pStream->avail_out;
+ default:
+ return 0;
+ }
+}
+
+void Deflater::setInputSegment( const uno::Sequence< sal_Int8 >& rBuffer )
+{
+ sInBuffer = rBuffer;
+ nOffset = 0;
+ nLength = rBuffer.getLength();
+}
+
+bool Deflater::needsInput() const
+{
+ return nLength <=0;
+}
+void Deflater::finish( )
+{
+ bFinish = true;
+}
+sal_Int32 Deflater::doDeflateSegment( uno::Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewLength )
+{
+ OSL_ASSERT( !(nNewLength < 0 || nNewLength > rBuffer.getLength()));
+ return doDeflateBytes(rBuffer, /*nNewOffset*/0, nNewLength);
+}
+sal_Int64 Deflater::getTotalIn() const
+{
+ return pStream->total_in + nTotalIn64;
+}
+sal_Int64 Deflater::getTotalOut() const
+{
+ return pStream->total_out + nTotalOut64;
+}
+void Deflater::reset( )
+{
+#if !defined Z_PREFIX
+ deflateReset(pStream.get());
+#else
+ z_deflateReset(pStream.get());
+#endif
+ bFinish = false;
+ bFinished = false;
+ nOffset = nLength = 0;
+}
+void Deflater::end( )
+{
+ if (pStream)
+ {
+#if !defined Z_PREFIX
+ deflateEnd(pStream.get());
+#else
+ z_deflateEnd(pStream.get());
+#endif
+ pStream.reset();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/Inflater.cxx b/package/source/zipapi/Inflater.cxx
new file mode 100644
index 0000000000..d03fed8c09
--- /dev/null
+++ b/package/source/zipapi/Inflater.cxx
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <package/Inflater.hxx>
+#include <zlib.h>
+#include <string.h>
+
+using namespace com::sun::star::uno;
+using namespace ZipUtils;
+
+/** Provides general purpose decompression using the ZLIB library */
+
+Inflater::Inflater(bool bNoWrap)
+: bFinished(false),
+ bNeedDict(false),
+ nOffset(0),
+ nLength(0),
+ nLastInflateError(0)
+{
+ pStream.reset(new z_stream);
+ /* memset to 0 to set zalloc/opaque etc */
+ memset (pStream.get(), 0, sizeof(*pStream));
+ sal_Int32 nRes;
+ nRes = inflateInit2(pStream.get(), bNoWrap ? -MAX_WBITS : MAX_WBITS);
+ switch (nRes)
+ {
+ case Z_OK:
+ break;
+ case Z_MEM_ERROR:
+ pStream.reset();
+ break;
+ case Z_STREAM_ERROR:
+ pStream.reset();
+ break;
+ default:
+ break;
+ }
+}
+
+Inflater::~Inflater()
+{
+ end();
+}
+
+void Inflater::setInput( const Sequence< sal_Int8 >& rBuffer )
+{
+ sInBuffer = rBuffer;
+ nOffset = 0;
+ nLength = rBuffer.getLength();
+}
+
+
+sal_Int32 Inflater::doInflateSegment( Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength )
+{
+ if (nNewOffset < 0 || nNewLength < 0 || nNewOffset + nNewLength > rBuffer.getLength())
+ {
+ // do error handling
+ }
+ return doInflateBytes(rBuffer, nNewOffset, nNewLength);
+}
+
+void Inflater::end( )
+{
+ if (pStream)
+ {
+#if !defined Z_PREFIX
+ inflateEnd(pStream.get());
+#else
+ z_inflateEnd(pStream.get());
+#endif
+ pStream.reset();
+ }
+}
+
+sal_Int32 Inflater::doInflateBytes (Sequence < sal_Int8 > &rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength)
+{
+ if ( !pStream )
+ {
+ nLastInflateError = Z_STREAM_ERROR;
+ return 0;
+ }
+
+ nLastInflateError = 0;
+
+ pStream->next_in = reinterpret_cast<const unsigned char*>( sInBuffer.getConstArray() + nOffset );
+ pStream->avail_in = nLength;
+ pStream->next_out = reinterpret_cast < unsigned char* > ( rBuffer.getArray() + nNewOffset );
+ pStream->avail_out = nNewLength;
+
+#if !defined Z_PREFIX
+ sal_Int32 nResult = ::inflate(pStream.get(), Z_PARTIAL_FLUSH);
+#else
+ sal_Int32 nResult = ::z_inflate(pStream.get(), Z_PARTIAL_FLUSH);
+#endif
+
+ switch (nResult)
+ {
+ case Z_STREAM_END:
+ bFinished = true;
+ [[fallthrough]];
+ case Z_OK:
+ nOffset += nLength - pStream->avail_in;
+ nLength = pStream->avail_in;
+ return nNewLength - pStream->avail_out;
+
+ case Z_NEED_DICT:
+ bNeedDict = true;
+ nOffset += nLength - pStream->avail_in;
+ nLength = pStream->avail_in;
+ return 0;
+
+ default:
+ // it is no error, if there is no input or no output
+ if ( nLength && nNewLength )
+ nLastInflateError = nResult;
+ }
+
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/MemoryByteGrabber.hxx b/package/source/zipapi/MemoryByteGrabber.hxx
new file mode 100644
index 0000000000..d474be40cd
--- /dev/null
+++ b/package/source/zipapi/MemoryByteGrabber.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_MEMORYBYTEGRABBER_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_MEMORYBYTEGRABBER_HXX
+
+#include <com/sun/star/uno/Sequence.h>
+
+class MemoryByteGrabber final
+{
+ const sal_Int8 *mpBuffer;
+ sal_Int32 mnCurrent, mnEnd;
+public:
+ MemoryByteGrabber ( const css::uno::Sequence < sal_Int8 > & rBuffer )
+ : mpBuffer ( rBuffer.getConstArray() )
+ , mnCurrent ( 0 )
+ , mnEnd ( rBuffer.getLength() )
+ {
+ }
+ MemoryByteGrabber(css::uno::Sequence<sal_Int8> &&) = delete;
+
+ const sal_Int8 * getCurrentPos () const { return mpBuffer + mnCurrent; }
+
+ sal_Int32 remainingSize() const { return mnEnd - mnCurrent; }
+
+ // XInputStream chained
+
+ /// @throws css::io::NotConnectedException
+ /// @throws css::io::BufferSizeExceededException
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void skipBytes( sal_Int32 nBytesToSkip )
+ {
+ mnCurrent += nBytesToSkip;
+ }
+
+ // XSeekable chained...
+ sal_Int16 ReadInt16()
+ {
+ if (mnCurrent + 2 > mnEnd )
+ return 0;
+ sal_Int16 nInt16 = mpBuffer[mnCurrent++] & 0xFF;
+ nInt16 |= ( mpBuffer[mnCurrent++] & 0xFF ) << 8;
+ return nInt16;
+ }
+
+ sal_Int16 ReadUInt16()
+ {
+ if (mnCurrent + 2 > mnEnd )
+ return 0;
+ sal_uInt16 nInt16 = mpBuffer[mnCurrent++] & 0xFF;
+ nInt16 |= ( mpBuffer[mnCurrent++] & 0xFF ) << 8;
+ return nInt16;
+ }
+
+ sal_Int32 ReadInt32()
+ {
+ if (mnCurrent + 4 > mnEnd )
+ return 0;
+
+ sal_Int32 nInt32 = mpBuffer[mnCurrent++] & 0xFF;
+ nInt32 |= ( mpBuffer[mnCurrent++] & 0xFF ) << 8;
+ nInt32 |= ( mpBuffer[mnCurrent++] & 0xFF ) << 16;
+ nInt32 |= ( mpBuffer[mnCurrent++] & 0xFF ) << 24;
+ return nInt32;
+ }
+
+ sal_uInt32 ReadUInt32()
+ {
+ if (mnCurrent + 4 > mnEnd )
+ return 0;
+
+ sal_uInt32 nInt32 = mpBuffer [mnCurrent++] & 0xFF;
+ nInt32 |= ( mpBuffer [mnCurrent++] & 0xFF ) << 8;
+ nInt32 |= ( mpBuffer [mnCurrent++] & 0xFF ) << 16;
+ nInt32 |= ( mpBuffer [mnCurrent++] & 0xFF ) << 24;
+ return nInt32;
+ }
+
+ sal_uInt64 ReadUInt64()
+ {
+ if (mnCurrent + 8 > mnEnd)
+ return 0;
+
+ sal_uInt64 nInt64 = mpBuffer[mnCurrent++] & 0xFF;
+ nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 8;
+ nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 16;
+ nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 24;
+ nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 32;
+ nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 40;
+ nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 48;
+ nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 56;
+ return nInt64;
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ThreadedDeflater.cxx b/package/source/zipapi/ThreadedDeflater.cxx
new file mode 100644
index 0000000000..f574105ad4
--- /dev/null
+++ b/package/source/zipapi/ThreadedDeflater.cxx
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ThreadedDeflater.hxx>
+#include <zlib.h>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <sal/log.hxx>
+
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star;
+
+namespace ZipUtils
+{
+const sal_Int64 MaxBlockSize = 128 * 1024;
+
+// Parallel ZLIB compression using threads. The class internally splits the data into
+// blocks and spawns ThreadPool tasks to process them independently. This is achieved
+// in a similar way how pigz works, see comments from Mark Adler at
+// https://stackoverflow.com/questions/30294766/how-to-use-multiple-threads-for-zlib-compression
+// and
+// https://stackoverflow.com/questions/30794053/how-to-use-multiple-threads-for-zlib-compression-same-input-source
+
+// Everything here should be either read-only, or writing to distinct data, or atomic.
+
+class ThreadedDeflater::Task : public comphelper::ThreadTask
+{
+ z_stream stream;
+ ThreadedDeflater* deflater;
+ int sequence;
+ int blockSize;
+ bool firstTask : 1;
+ bool lastTask : 1;
+
+public:
+ Task(ThreadedDeflater* deflater_, int sequence_, int blockSize_, bool firstTask_,
+ bool lastTask_)
+ : comphelper::ThreadTask(deflater_->threadTaskTag)
+ , stream()
+ , deflater(deflater_)
+ , sequence(sequence_)
+ , blockSize(blockSize_)
+ , firstTask(firstTask_)
+ , lastTask(lastTask_)
+ {
+ }
+
+private:
+ virtual void doWork() override;
+};
+
+ThreadedDeflater::ThreadedDeflater(sal_Int32 nSetLevel)
+ : threadTaskTag(comphelper::ThreadPool::createThreadTaskTag())
+ , totalIn(0)
+ , totalOut(0)
+ , zlibLevel(nSetLevel)
+{
+}
+
+ThreadedDeflater::~ThreadedDeflater() COVERITY_NOEXCEPT_FALSE { clear(); }
+
+void ThreadedDeflater::deflateWrite(
+ const css::uno::Reference<css::io::XInputStream>& xInStream,
+ std::function<void(const css::uno::Sequence<sal_Int8>&, sal_Int32)> aProcessInputFunc,
+ std::function<void(const css::uno::Sequence<sal_Int8>&, sal_Int32)> aProcessOutputFunc)
+{
+ sal_Int64 nThreadCount = comphelper::ThreadPool::getSharedOptimalPool().getWorkerCount();
+ sal_Int64 batchSize = MaxBlockSize * nThreadCount;
+ inBuffer.realloc(batchSize);
+ prevDataBlock.realloc(MaxBlockSize);
+ outBuffers.resize(nThreadCount);
+ maProcessOutputFunc = aProcessOutputFunc;
+ bool firstTask = true;
+
+ while (xInStream->available() > 0)
+ {
+ sal_Int64 inputBytes = xInStream->readBytes(inBuffer, batchSize);
+ aProcessInputFunc(inBuffer, inputBytes);
+ totalIn += inputBytes;
+ int sequence = 0;
+ bool lastBatch = xInStream->available() <= 0;
+ sal_Int64 bytesPending = inputBytes;
+ while (bytesPending > 0)
+ {
+ sal_Int64 taskSize = std::min(MaxBlockSize, bytesPending);
+ bytesPending -= taskSize;
+ bool lastTask = lastBatch && !bytesPending;
+ comphelper::ThreadPool::getSharedOptimalPool().pushTask(
+ std::make_unique<Task>(this, sequence++, taskSize, firstTask, lastTask));
+
+ if (firstTask)
+ firstTask = false;
+ }
+
+ assert(bytesPending == 0);
+
+ comphelper::ThreadPool::getSharedOptimalPool().waitUntilDone(threadTaskTag);
+
+ if (!lastBatch)
+ {
+ assert(inputBytes == batchSize);
+ std::copy_n(std::cbegin(inBuffer) + (batchSize - MaxBlockSize), MaxBlockSize,
+ prevDataBlock.getArray());
+ }
+
+ processDeflatedBuffers();
+ }
+}
+
+void ThreadedDeflater::processDeflatedBuffers()
+{
+ sal_Int64 batchOutputSize = 0;
+ for (const auto& buffer : outBuffers)
+ batchOutputSize += buffer.size();
+
+ css::uno::Sequence<sal_Int8> outBuffer(batchOutputSize);
+
+ auto pos = outBuffer.getArray();
+ for (auto& buffer : outBuffers)
+ {
+ pos = std::copy(buffer.begin(), buffer.end(), pos);
+ buffer.clear();
+ }
+
+ maProcessOutputFunc(outBuffer, batchOutputSize);
+ totalOut += batchOutputSize;
+}
+
+void ThreadedDeflater::clear()
+{
+ inBuffer = uno::Sequence<sal_Int8>();
+ outBuffers.clear();
+}
+
+#if defined Z_PREFIX
+#define deflateInit2 z_deflateInit2
+#define deflateBound z_deflateBound
+#define deflateSetDictionary z_deflateSetDictionary
+#define deflate z_deflate
+#define deflateEnd z_deflateEnd
+#endif
+
+void ThreadedDeflater::Task::doWork()
+{
+ stream.zalloc = nullptr;
+ stream.zfree = nullptr;
+ stream.opaque = nullptr;
+ // -MAX_WBITS means 32k window size and raw stream
+ if (deflateInit2(&stream, deflater->zlibLevel, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY)
+ != Z_OK)
+ {
+ SAL_WARN("package.threadeddeflate", "deflateInit2() failed");
+ abort();
+ }
+ // Find out size for our output buffer to be large enough for deflate() needing to be called just once.
+ sal_Int64 outputMaxSize = deflateBound(&stream, blockSize);
+ // add extra size for Z_SYNC_FLUSH
+ outputMaxSize += 20;
+ deflater->outBuffers[sequence].resize(outputMaxSize);
+ sal_Int64 myInBufferStart = sequence * MaxBlockSize;
+ // zlib doesn't handle const properly
+ unsigned char* inBufferPtr = reinterpret_cast<unsigned char*>(
+ const_cast<signed char*>(deflater->inBuffer.getConstArray()));
+ if (!firstTask)
+ {
+ // the window size is 32k, so set last 32k of previous data as the dictionary
+ assert(MAX_WBITS == 15);
+ assert(MaxBlockSize >= 32768);
+ if (sequence > 0)
+ {
+ deflateSetDictionary(&stream, inBufferPtr + myInBufferStart - 32768, 32768);
+ }
+ else
+ {
+ unsigned char* prevBufferPtr = reinterpret_cast<unsigned char*>(
+ const_cast<signed char*>(deflater->prevDataBlock.getConstArray()));
+ deflateSetDictionary(&stream, prevBufferPtr + MaxBlockSize - 32768, 32768);
+ }
+ }
+ stream.next_in = inBufferPtr + myInBufferStart;
+ stream.avail_in = blockSize;
+ stream.next_out = reinterpret_cast<unsigned char*>(deflater->outBuffers[sequence].data());
+ stream.avail_out = outputMaxSize;
+
+ // The trick is in using Z_SYNC_FLUSH instead of Z_NO_FLUSH. It will align the data at a byte boundary,
+ // and since we use a raw stream, the data blocks then can be simply concatenated.
+ int res = deflate(&stream, lastTask ? Z_FINISH : Z_SYNC_FLUSH);
+ assert(stream.avail_in == 0); // Check that everything has been deflated.
+ if (lastTask ? res == Z_STREAM_END : res == Z_OK)
+ { // ok
+ sal_Int64 outSize = outputMaxSize - stream.avail_out;
+ deflater->outBuffers[sequence].resize(outSize);
+ }
+ else
+ {
+ SAL_WARN("package.threadeddeflate", "deflate() failed");
+ abort();
+ }
+ deflateEnd(&stream);
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/XBufferedThreadedStream.cxx b/package/source/zipapi/XBufferedThreadedStream.cxx
new file mode 100644
index 0000000000..d3bf995d90
--- /dev/null
+++ b/package/source/zipapi/XBufferedThreadedStream.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "XBufferedThreadedStream.hxx"
+
+using namespace css::uno;
+
+namespace {
+
+class UnzippingThread: public salhelper::Thread
+{
+ XBufferedThreadedStream &mxStream;
+public:
+ explicit UnzippingThread(XBufferedThreadedStream &xStream): Thread("Unzipping"), mxStream(xStream) {}
+private:
+ virtual void execute() override
+ {
+ try
+ {
+ mxStream.produce();
+ }
+ catch (...)
+ {
+ mxStream.saveException(std::current_exception());
+ }
+
+ mxStream.setTerminateThread();
+ }
+};
+
+}
+
+XBufferedThreadedStream::XBufferedThreadedStream(
+ const Reference<XInputStream>& xSrcStream,
+ sal_Int64 nStreamSize)
+: mxSrcStream( xSrcStream )
+, mnPos(0)
+, mnStreamSize( nStreamSize )
+, mnOffset( 0 )
+, mxUnzippingThread( new UnzippingThread(*this) )
+, mbTerminateThread( false )
+{
+ mxUnzippingThread->launch();
+}
+
+XBufferedThreadedStream::~XBufferedThreadedStream()
+{
+ setTerminateThread();
+ mxUnzippingThread->join();
+}
+
+/**
+ * Reads from UnbufferedStream in a separate thread and stores the buffer blocks
+ * in maPendingBuffers queue for further use.
+ */
+void XBufferedThreadedStream::produce()
+{
+ Buffer pProducedBuffer;
+ sal_Int64 nTotalBytesRead(0);
+ std::unique_lock<std::mutex> aGuard( maBufferProtector );
+ do
+ {
+ if( !maUsedBuffers.empty() )
+ {
+ pProducedBuffer = maUsedBuffers.front();
+ maUsedBuffers.pop();
+ }
+
+ aGuard.unlock();
+ nTotalBytesRead += mxSrcStream->readBytes( pProducedBuffer, nBufferSize );
+
+ aGuard.lock();
+ maPendingBuffers.push( pProducedBuffer );
+ maBufferConsumeResume.notify_one();
+
+ if (!mbTerminateThread)
+ maBufferProduceResume.wait( aGuard, [&]{return canProduce(); } );
+
+ } while( !mbTerminateThread && nTotalBytesRead < mnStreamSize );
+}
+
+/**
+ * Fetches next available block from maPendingBuffers for use in Reading thread.
+ */
+const Buffer& XBufferedThreadedStream::getNextBlock()
+{
+ std::unique_lock<std::mutex> aGuard( maBufferProtector );
+ const sal_Int32 nBufSize = maInUseBuffer.getLength();
+ if( nBufSize <= 0 || mnOffset >= nBufSize )
+ {
+ if( mnOffset >= nBufSize )
+ maUsedBuffers.push( maInUseBuffer );
+
+ maBufferConsumeResume.wait( aGuard, [&]{return canConsume(); } );
+
+ if( maPendingBuffers.empty() )
+ {
+ maInUseBuffer = Buffer();
+ if (maSavedException)
+ std::rethrow_exception(maSavedException);
+ }
+ else
+ {
+ maInUseBuffer = maPendingBuffers.front();
+ maPendingBuffers.pop();
+ mnOffset = 0;
+
+ if( maPendingBuffers.size() <= nBufferLowWater )
+ maBufferProduceResume.notify_one();
+ }
+ }
+
+ return maInUseBuffer;
+}
+
+void XBufferedThreadedStream::setTerminateThread()
+{
+ std::scoped_lock<std::mutex> aGuard( maBufferProtector );
+ mbTerminateThread = true;
+ maBufferProduceResume.notify_one();
+ maBufferConsumeResume.notify_one();
+}
+
+sal_Int32 SAL_CALL XBufferedThreadedStream::readBytes( Sequence< sal_Int8 >& rData, sal_Int32 nBytesToRead )
+{
+ if( !hasBytes() )
+ return 0;
+
+ const sal_Int32 nAvailableSize = static_cast< sal_Int32 > ( std::min< sal_Int64 >( nBytesToRead, remainingSize() ) );
+ rData.realloc( nAvailableSize );
+ auto pData = rData.getArray();
+ sal_Int32 i = 0, nPendingBytes = nAvailableSize;
+
+ while( nPendingBytes )
+ {
+ const Buffer &pBuffer = getNextBlock();
+ if( !pBuffer.hasElements() )
+ {
+ rData.realloc( nAvailableSize - nPendingBytes );
+ return nAvailableSize - nPendingBytes;
+ }
+ const sal_Int32 limit = std::min<sal_Int32>( nPendingBytes, pBuffer.getLength() - mnOffset );
+
+ memcpy( &pData[i], &pBuffer[mnOffset], limit );
+
+ nPendingBytes -= limit;
+ mnOffset += limit;
+ mnPos += limit;
+ i += limit;
+ }
+
+ return nAvailableSize;
+}
+
+sal_Int32 SAL_CALL XBufferedThreadedStream::readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ return readBytes( aData, nMaxBytesToRead );
+}
+void SAL_CALL XBufferedThreadedStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ if( nBytesToSkip )
+ {
+ Sequence < sal_Int8 > aSequence( nBytesToSkip );
+ readBytes( aSequence, nBytesToSkip );
+ }
+}
+
+sal_Int32 SAL_CALL XBufferedThreadedStream::available()
+{
+ if( !hasBytes() )
+ return 0;
+
+ return static_cast< sal_Int32 > ( std::min< sal_Int64 >( SAL_MAX_INT32, remainingSize() ) );
+}
+
+void SAL_CALL XBufferedThreadedStream::closeInput()
+{
+ setTerminateThread();
+ mxUnzippingThread->join();
+ mxSrcStream->closeInput();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/XBufferedThreadedStream.hxx b/package/source/zipapi/XBufferedThreadedStream.hxx
new file mode 100644
index 0000000000..beb1cd33c7
--- /dev/null
+++ b/package/source/zipapi/XBufferedThreadedStream.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_XBUFFEREDTHREADEDSTREAM_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_XBUFFEREDTHREADEDSTREAM_HXX
+
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/thread.hxx>
+
+#include <queue>
+#include <mutex>
+#include <condition_variable>
+#include <exception>
+
+typedef css::uno::Sequence< sal_Int8 > Buffer;
+
+class XBufferedThreadedStream : public cppu::WeakImplHelper< css::io::XInputStream >
+{
+private:
+ const css::uno::Reference<XInputStream> mxSrcStream;
+ sal_Int64 mnPos; /// position in stream
+ sal_Int64 mnStreamSize; /// available size of stream
+
+ Buffer maInUseBuffer; /// Buffer block in use
+ int mnOffset; /// position in maInUseBuffer
+ std::queue < Buffer > maPendingBuffers; /// Buffers that are available for use
+ std::queue < Buffer > maUsedBuffers;
+
+ rtl::Reference< salhelper::Thread > mxUnzippingThread;
+ std::mutex maBufferProtector; /// mutex protecting Buffer queues.
+ std::condition_variable maBufferConsumeResume;
+ std::condition_variable maBufferProduceResume;
+ bool mbTerminateThread; /// indicates the failure of one of the threads
+
+ std::exception_ptr maSavedException; /// exception caught during unzipping is saved to be thrown during reading
+
+ static const size_t nBufferLowWater = 2;
+ static const size_t nBufferHighWater = 4;
+ static const size_t nBufferSize = 32 * 1024;
+
+ const Buffer& getNextBlock();
+ sal_Int64 remainingSize() const { return mnStreamSize - mnPos; }
+ bool hasBytes() const { return mnPos < mnStreamSize; }
+
+ bool canProduce() const
+ {
+ return( mbTerminateThread || maPendingBuffers.size() < nBufferHighWater );
+ }
+
+ bool canConsume() const
+ {
+ return( mbTerminateThread || !maPendingBuffers.empty() );
+ }
+
+public:
+ XBufferedThreadedStream(
+ const css::uno::Reference<XInputStream>& xSrcStream,
+ sal_Int64 nStreamSize /* cf. sal_Int32 available(); */ );
+
+ virtual ~XBufferedThreadedStream() override;
+
+ void produce();
+ void setTerminateThread();
+ void saveException(const std::exception_ptr& exception) { maSavedException = exception; }
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/XUnbufferedStream.cxx b/package/source/zipapi/XUnbufferedStream.cxx
new file mode 100644
index 0000000000..8b628b14dd
--- /dev/null
+++ b/package/source/zipapi/XUnbufferedStream.cxx
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+
+#include "XUnbufferedStream.hxx"
+#include <EncryptionData.hxx>
+#include <ZipFile.hxx>
+#include <EncryptedDataHeader.hxx>
+#include <algorithm>
+#include <string.h>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+#include <utility>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star;
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using com::sun::star::packages::zip::ZipIOException;
+
+XUnbufferedStream::XUnbufferedStream(
+ const uno::Reference< uno::XComponentContext >& xContext,
+ rtl::Reference< comphelper::RefCountedMutex > aMutexHolder,
+ ZipEntry const & rEntry,
+ Reference < XInputStream > const & xNewZipStream,
+ const ::rtl::Reference< EncryptionData >& rData,
+ sal_Int8 nStreamMode,
+ bool bIsEncrypted,
+ const OUString& aMediaType,
+ bool bRecoveryMode )
+: maMutexHolder(std::move( aMutexHolder ))
+, mxZipStream ( xNewZipStream )
+, mxZipSeek ( xNewZipStream, UNO_QUERY )
+, maEntry ( rEntry )
+, mnBlockSize( 1 )
+, maInflater ( true )
+, mbRawStream ( nStreamMode == UNBUFF_STREAM_RAW || nStreamMode == UNBUFF_STREAM_WRAPPEDRAW )
+, mbWrappedRaw ( nStreamMode == UNBUFF_STREAM_WRAPPEDRAW )
+, mnHeaderToRead ( 0 )
+, mnZipCurrent ( 0 )
+, mnZipEnd ( 0 )
+, mnZipSize ( 0 )
+, mnMyCurrent ( 0 )
+, mbCheckCRC(!bRecoveryMode)
+{
+ mnZipCurrent = maEntry.nOffset;
+ sal_Int64 nSize;
+ if ( mbRawStream )
+ {
+ mnZipSize = maEntry.nMethod == DEFLATED ? maEntry.nCompressedSize : maEntry.nSize;
+ nSize = mnZipSize;
+ }
+ else
+ {
+ mnZipSize = maEntry.nSize;
+ nSize = maEntry.nMethod == DEFLATED ? maEntry.nCompressedSize : maEntry.nSize;
+ }
+
+ if (mnZipSize < 0)
+ throw ZipIOException("The stream seems to be broken!");
+
+ if (o3tl::checked_add(maEntry.nOffset, nSize, mnZipEnd))
+ throw ZipIOException("Integer-overflow");
+
+ bool bHaveEncryptData = rData.is() && rData->m_aInitVector.hasElements() &&
+ ((rData->m_aSalt.hasElements() && (rData->m_oPBKDFIterationCount || rData->m_oArgon2Args))
+ ||
+ rData->m_aKey.hasElements());
+ bool bMustDecrypt = nStreamMode == UNBUFF_STREAM_DATA && bHaveEncryptData && bIsEncrypted;
+
+ if ( bMustDecrypt )
+ {
+ m_xCipherContext = ZipFile::StaticGetCipher( xContext, rData, false );
+ // this is only relevant when padding is used
+ mnBlockSize = ( rData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING ? 16 : 1 );
+ }
+
+ if ( !(bHaveEncryptData && mbWrappedRaw && bIsEncrypted) )
+ return;
+
+ // if we have the data needed to decrypt it, but didn't want it decrypted (or
+ // we couldn't decrypt it due to wrong password), then we prepend this
+ // data to the stream
+
+ // Make a buffer big enough to hold both the header and the data itself
+ maHeader.realloc ( n_ConstHeaderSize +
+ rData->m_aInitVector.getLength() +
+ rData->m_aSalt.getLength() +
+ rData->m_aDigest.getLength() +
+ aMediaType.getLength() * sizeof( sal_Unicode ) );
+ sal_Int8 * pHeader = maHeader.getArray();
+ ZipFile::StaticFillHeader( rData, rEntry.nSize, aMediaType, pHeader );
+ mnHeaderToRead = static_cast < sal_Int16 > ( maHeader.getLength() );
+ mnZipSize += mnHeaderToRead;
+}
+
+// allows to read package raw stream
+XUnbufferedStream::XUnbufferedStream(
+ rtl::Reference< comphelper::RefCountedMutex > aMutexHolder,
+ const Reference < XInputStream >& xRawStream,
+ const ::rtl::Reference< EncryptionData >& rData )
+: maMutexHolder(std::move( aMutexHolder ))
+, mxZipStream ( xRawStream )
+, mxZipSeek ( xRawStream, UNO_QUERY )
+, mnBlockSize( 1 )
+, maInflater ( true )
+, mbRawStream ( false )
+, mbWrappedRaw ( false )
+, mnHeaderToRead ( 0 )
+, mnZipCurrent ( 0 )
+, mnZipEnd ( 0 )
+, mnZipSize ( 0 )
+, mnMyCurrent ( 0 )
+, mbCheckCRC( false )
+{
+ // for this scenario maEntry is not set !!!
+ OSL_ENSURE( mxZipSeek.is(), "The stream must be seekable!" );
+
+ // skip raw header, it must be already parsed to rData
+ mnZipCurrent = n_ConstHeaderSize + rData->m_aInitVector.getLength() +
+ rData->m_aSalt.getLength() + rData->m_aDigest.getLength();
+
+ try {
+ if ( mxZipSeek.is() )
+ mnZipSize = mxZipSeek->getLength();
+ } catch( const Exception& )
+ {
+ // in case of problem the size will stay set to 0
+ TOOLS_WARN_EXCEPTION("package", "ignoring");
+ }
+
+ mnZipEnd = mnZipCurrent + mnZipSize;
+
+ // the raw data will not be decrypted, no need for the cipher
+ // m_xCipherContext = ZipFile::StaticGetCipher( xContext, rData, false );
+}
+
+XUnbufferedStream::~XUnbufferedStream()
+{
+}
+
+sal_Int32 SAL_CALL XUnbufferedStream::readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ ::osl::MutexGuard aGuard( maMutexHolder->GetMutex() );
+
+ sal_Int32 nRequestedBytes = nBytesToRead;
+ OSL_ENSURE( !mnHeaderToRead || mbWrappedRaw, "Only encrypted raw stream can be provided with header!" );
+ if ( mnMyCurrent + nRequestedBytes > mnZipSize + maHeader.getLength() )
+ nRequestedBytes = static_cast < sal_Int32 > ( mnZipSize + maHeader.getLength() - mnMyCurrent );
+
+ sal_Int32 nTotal = 0;
+ aData.realloc ( nRequestedBytes );
+ if ( nRequestedBytes )
+ {
+ sal_Int32 nRead = 0;
+ sal_Int32 nLastRead = 0;
+ if ( mbRawStream )
+ {
+ sal_Int64 nDiff = mnZipEnd - mnZipCurrent;
+
+ if ( mbWrappedRaw && mnHeaderToRead )
+ {
+ sal_Int16 nHeadRead = static_cast< sal_Int16 >(( nRequestedBytes > mnHeaderToRead ?
+ mnHeaderToRead : nRequestedBytes ));
+ memcpy ( aData.getArray(), maHeader.getConstArray() + maHeader.getLength() - mnHeaderToRead, nHeadRead );
+ mnHeaderToRead = mnHeaderToRead - nHeadRead;
+
+ if ( nHeadRead < nRequestedBytes )
+ {
+ sal_Int32 nToRead = nRequestedBytes - nHeadRead;
+ nToRead = ( nDiff < nToRead ) ? sal::static_int_cast< sal_Int32 >( nDiff ) : nToRead;
+
+ Sequence< sal_Int8 > aPureData( nToRead );
+ mxZipSeek->seek ( mnZipCurrent );
+ nRead = mxZipStream->readBytes ( aPureData, nToRead );
+ mnZipCurrent += nRead;
+
+ aPureData.realloc( nRead );
+ if ( mbCheckCRC )
+ maCRC.update( aPureData );
+
+ aData.realloc( nHeadRead + nRead );
+
+ const sal_Int8* pPureBuffer = aPureData.getConstArray();
+ sal_Int8* pBuffer = aData.getArray();
+ for ( sal_Int32 nInd = 0; nInd < nRead; nInd++ )
+ pBuffer[ nHeadRead + nInd ] = pPureBuffer[ nInd ];
+ }
+
+ nRead += nHeadRead;
+ }
+ else
+ {
+ mxZipSeek->seek ( mnZipCurrent );
+
+ nRead = mxZipStream->readBytes (
+ aData,
+ std::min<sal_Int64>(nDiff, nRequestedBytes) );
+
+ mnZipCurrent += nRead;
+
+ aData.realloc( nRead );
+ if ( mbWrappedRaw && mbCheckCRC )
+ maCRC.update( aData );
+ }
+ }
+ else
+ {
+ for (;;)
+ {
+ nLastRead = maInflater.doInflateSegment( aData, nRead, aData.getLength() - nRead );
+ if ( 0 != nLastRead && ( nRead + nLastRead == nRequestedBytes || mnZipCurrent >= mnZipEnd ) )
+ break;
+ nRead += nLastRead;
+ if ( nRead > nRequestedBytes )
+ throw RuntimeException(
+ "Should not be possible to read more than requested!" );
+
+ if ( maInflater.finished() || maInflater.getLastInflateError() )
+ throw ZipIOException("The stream seems to be broken!" );
+
+ if ( maInflater.needsDictionary() )
+ throw ZipIOException("Dictionaries are not supported!" );
+
+ sal_Int32 nDiff = static_cast< sal_Int32 >( mnZipEnd - mnZipCurrent );
+ if ( nDiff <= 0 )
+ {
+ throw ZipIOException("The stream seems to be broken!" );
+ }
+
+ mxZipSeek->seek ( mnZipCurrent );
+
+ sal_Int32 nToRead = std::max( nRequestedBytes, static_cast< sal_Int32 >( 8192 ) );
+ if ( mnBlockSize > 1 )
+ nToRead = nToRead + mnBlockSize - nToRead % mnBlockSize;
+ nToRead = std::min( nDiff, nToRead );
+
+ sal_Int32 nZipRead = mxZipStream->readBytes( maCompBuffer, nToRead );
+ if ( nZipRead < nToRead )
+ throw ZipIOException("No expected data!" );
+
+ mnZipCurrent += nZipRead;
+ // maCompBuffer now has the data, check if we need to decrypt
+ // before passing to the Inflater
+ if ( m_xCipherContext.is() )
+ {
+ if ( mbCheckCRC )
+ maCRC.update( maCompBuffer );
+
+ maCompBuffer = m_xCipherContext->convertWithCipherContext( maCompBuffer );
+ if ( mnZipCurrent == mnZipEnd )
+ {
+ // this should throw if AEAD is in use and the tag fails to validate
+ uno::Sequence< sal_Int8 > aSuffix = m_xCipherContext->finalizeCipherContextAndDispose();
+ if ( aSuffix.hasElements() )
+ {
+ sal_Int32 nOldLen = maCompBuffer.getLength();
+ maCompBuffer.realloc( nOldLen + aSuffix.getLength() );
+ memcpy( maCompBuffer.getArray() + nOldLen, aSuffix.getConstArray(), aSuffix.getLength() );
+ }
+ }
+ }
+ maInflater.setInput ( maCompBuffer );
+
+ }
+ }
+
+ mnMyCurrent += nRead + nLastRead;
+ nTotal = nRead + nLastRead;
+ if ( nTotal < nRequestedBytes)
+ aData.realloc ( nTotal );
+
+ if ( mbCheckCRC && ( !mbRawStream || mbWrappedRaw ) )
+ {
+ if ( !m_xCipherContext.is() && !mbWrappedRaw )
+ maCRC.update( aData );
+
+ if ( mnZipSize + maHeader.getLength() == mnMyCurrent && maCRC.getValue() != maEntry.nCrc )
+ throw ZipIOException("The stream seems to be broken!" );
+ }
+ }
+
+ return nTotal;
+}
+
+sal_Int32 SAL_CALL XUnbufferedStream::readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ return readBytes ( aData, nMaxBytesToRead );
+}
+void SAL_CALL XUnbufferedStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ if ( nBytesToSkip )
+ {
+ Sequence < sal_Int8 > aSequence ( nBytesToSkip );
+ readBytes ( aSequence, nBytesToSkip );
+ }
+}
+
+sal_Int32 SAL_CALL XUnbufferedStream::available( )
+{
+ //available size must include the prepended header in case of wrapped raw stream
+ return static_cast< sal_Int32 > ( std::min< sal_Int64 >( SAL_MAX_INT32, (mnZipSize + mnHeaderToRead - mnMyCurrent) ) );
+}
+
+void SAL_CALL XUnbufferedStream::closeInput( )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/XUnbufferedStream.hxx b/package/source/zipapi/XUnbufferedStream.hxx
new file mode 100644
index 0000000000..af57706386
--- /dev/null
+++ b/package/source/zipapi/XUnbufferedStream.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_XUNBUFFEREDSTREAM_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_XUNBUFFEREDSTREAM_HXX
+
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+
+#include <comphelper/refcountedmutex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <package/Inflater.hxx>
+#include <ZipEntry.hxx>
+#include <CRC32.hxx>
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+#define UNBUFF_STREAM_DATA 0
+#define UNBUFF_STREAM_RAW 1
+#define UNBUFF_STREAM_WRAPPEDRAW 2
+
+class EncryptionData;
+class XUnbufferedStream final : public cppu::WeakImplHelper
+<
+ css::io::XInputStream
+>
+{
+ rtl::Reference<comphelper::RefCountedMutex> maMutexHolder;
+
+ css::uno::Reference < css::io::XInputStream > mxZipStream;
+ css::uno::Reference < css::io::XSeekable > mxZipSeek;
+ css::uno::Sequence < sal_Int8 > maCompBuffer, maHeader;
+ ZipEntry maEntry;
+ sal_Int32 mnBlockSize;
+ css::uno::Reference< css::xml::crypto::XCipherContext > m_xCipherContext;
+ ZipUtils::Inflater maInflater;
+ bool mbRawStream, mbWrappedRaw;
+ sal_Int16 mnHeaderToRead;
+ sal_Int64 mnZipCurrent, mnZipEnd, mnZipSize, mnMyCurrent;
+ CRC32 maCRC;
+ bool mbCheckCRC;
+
+public:
+ XUnbufferedStream(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ rtl::Reference<comphelper::RefCountedMutex> aMutexHolder,
+ ZipEntry const & rEntry,
+ css::uno::Reference < css::io::XInputStream > const & xNewZipStream,
+ const ::rtl::Reference< EncryptionData >& rData,
+ sal_Int8 nStreamMode,
+ bool bIsEncrypted,
+ const OUString& aMediaType,
+ bool bRecoveryMode );
+
+ // allows to read package raw stream
+ XUnbufferedStream(
+ rtl::Reference<comphelper::RefCountedMutex> aMutexHolder,
+ const css::uno::Reference < css::io::XInputStream >& xRawStream,
+ const ::rtl::Reference< EncryptionData >& rData );
+
+ sal_Int64 getSize() const { return mnZipSize; }
+
+ virtual ~XUnbufferedStream() override;
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ZipEnumeration.cxx b/package/source/zipapi/ZipEnumeration.cxx
new file mode 100644
index 0000000000..0c036882f0
--- /dev/null
+++ b/package/source/zipapi/ZipEnumeration.cxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipEnumeration.hxx>
+
+/** Provides an Enumeration over the contents of a Zip file */
+
+ZipEnumeration::ZipEnumeration(EntryHash& rNewEntryHash)
+ : rEntryHash(rNewEntryHash)
+ , aIterator(rEntryHash.begin())
+{
+}
+bool ZipEnumeration::hasMoreElements() { return (aIterator != rEntryHash.end()); }
+
+const ZipEntry* ZipEnumeration::nextElement()
+{
+ if (aIterator != rEntryHash.end())
+ return &((*aIterator++).second);
+ else
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ZipFile.cxx b/package/source/zipapi/ZipFile.cxx
new file mode 100644
index 0000000000..474b73ff53
--- /dev/null
+++ b/package/source/zipapi/ZipFile.cxx
@@ -0,0 +1,1474 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/packages/NoEncryptionException.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/packages/zip/ZipException.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/NSSInitializer.hpp>
+
+#include <comphelper/bytereader.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/threadpool.hxx>
+#include <rtl/digest.h>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <iterator>
+#include <utility>
+#include <vector>
+
+#include <argon2.h>
+
+#include "blowfishcontext.hxx"
+#include "sha1context.hxx"
+#include <ZipFile.hxx>
+#include <ZipEnumeration.hxx>
+#include "XUnbufferedStream.hxx"
+#include "XBufferedThreadedStream.hxx"
+#include <PackageConstants.hxx>
+#include <EncryptedDataHeader.hxx>
+#include <EncryptionData.hxx>
+#include "MemoryByteGrabber.hxx"
+
+#include <CRC32.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::packages;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+using ZipUtils::Inflater;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+/** This class is used to read entries from a zip file
+ */
+ZipFile::ZipFile( rtl::Reference<comphelper::RefCountedMutex> aMutexHolder,
+ uno::Reference < XInputStream > const &xInput,
+ uno::Reference < XComponentContext > xContext,
+ bool bInitialise )
+: m_aMutexHolder(std::move( aMutexHolder ))
+, aGrabber( xInput )
+, aInflater( true )
+, xStream(xInput)
+, m_xContext (std::move( xContext ))
+, bRecoveryMode( false )
+{
+ if (bInitialise && readCEN() == -1 )
+ {
+ aEntries.clear();
+ throw ZipException( "stream data looks to be broken" );
+ }
+}
+
+ZipFile::ZipFile( rtl::Reference< comphelper::RefCountedMutex > aMutexHolder,
+ uno::Reference < XInputStream > const &xInput,
+ uno::Reference < XComponentContext > xContext,
+ bool bInitialise, bool bForceRecovery)
+: m_aMutexHolder(std::move( aMutexHolder ))
+, aGrabber( xInput )
+, aInflater( true )
+, xStream(xInput)
+, m_xContext (std::move( xContext ))
+, bRecoveryMode( bForceRecovery )
+{
+ if (bInitialise)
+ {
+ if ( bForceRecovery )
+ {
+ recover();
+ }
+ else if ( readCEN() == -1 )
+ {
+ aEntries.clear();
+ throw ZipException("stream data looks to be broken" );
+ }
+ }
+}
+
+ZipFile::~ZipFile()
+{
+ aEntries.clear();
+}
+
+void ZipFile::setInputStream ( const uno::Reference < XInputStream >& xNewStream )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ xStream = xNewStream;
+ aGrabber.setInputStream ( xStream );
+}
+
+uno::Reference< xml::crypto::XDigestContext > ZipFile::StaticGetDigestContextForChecksum( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData )
+{
+ assert(xEncryptionData->m_oCheckAlg); // callers checked it already
+
+ uno::Reference< xml::crypto::XDigestContext > xDigestContext;
+ if (*xEncryptionData->m_oCheckAlg == xml::crypto::DigestID::SHA256_1K)
+ {
+ uno::Reference< uno::XComponentContext > xContext = xArgContext;
+ if ( !xContext.is() )
+ xContext = comphelper::getProcessComponentContext();
+
+ uno::Reference< xml::crypto::XNSSInitializer > xDigestContextSupplier = xml::crypto::NSSInitializer::create( xContext );
+
+ xDigestContext.set(xDigestContextSupplier->getDigestContext(
+ *xEncryptionData->m_oCheckAlg, uno::Sequence<beans::NamedValue>()),
+ uno::UNO_SET_THROW);
+ }
+ else if (*xEncryptionData->m_oCheckAlg == xml::crypto::DigestID::SHA1_1K)
+ {
+ if (xEncryptionData->m_bTryWrongSHA1)
+ {
+ xDigestContext.set(StarOfficeSHA1DigestContext::Create(), uno::UNO_SET_THROW);
+ }
+ else
+ {
+ xDigestContext.set(CorrectSHA1DigestContext::Create(), uno::UNO_SET_THROW);
+ }
+ }
+
+ return xDigestContext;
+}
+
+uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData, bool bEncrypt )
+{
+ uno::Reference< xml::crypto::XCipherContext > xResult;
+
+ if (xEncryptionData->m_nDerivedKeySize < 0)
+ {
+ throw ZipIOException("Invalid derived key length!" );
+ }
+
+ uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize );
+ if (!xEncryptionData->m_oPBKDFIterationCount && !xEncryptionData->m_oArgon2Args
+ && xEncryptionData->m_nDerivedKeySize == xEncryptionData->m_aKey.getLength())
+ {
+ // gpg4libre: no need to derive key, m_aKey is already
+ // usable as symmetric session key
+ aDerivedKey = xEncryptionData->m_aKey;
+ }
+ else if (xEncryptionData->m_oArgon2Args)
+ {
+ // apparently multiple lanes cannot be processed in parallel (the
+ // implementation will clamp), but it doesn't make sense to have more
+ // threads than CPUs
+ uint32_t const threads(::comphelper::ThreadPool::getPreferredConcurrency());
+ // need to use context to set a fixed version
+ argon2_context context = {
+ .out = reinterpret_cast<uint8_t *>(aDerivedKey.getArray()),
+ .outlen = ::sal::static_int_cast<uint32_t>(aDerivedKey.getLength()),
+ .pwd = reinterpret_cast<uint8_t *>(xEncryptionData->m_aKey.getArray()),
+ .pwdlen = ::sal::static_int_cast<uint32_t>(xEncryptionData->m_aKey.getLength()),
+ .salt = reinterpret_cast<uint8_t *>(xEncryptionData->m_aSalt.getArray()),
+ .saltlen = ::sal::static_int_cast<uint32_t>(xEncryptionData->m_aSalt.getLength()),
+ .secret = nullptr, .secretlen = 0,
+ .ad = nullptr, .adlen = 0,
+ .t_cost = ::sal::static_int_cast<uint32_t>(::std::get<0>(*xEncryptionData->m_oArgon2Args)),
+ .m_cost = ::sal::static_int_cast<uint32_t>(::std::get<1>(*xEncryptionData->m_oArgon2Args)),
+ .lanes = ::sal::static_int_cast<uint32_t>(::std::get<2>(*xEncryptionData->m_oArgon2Args)),
+ .threads = threads,
+ .version = ARGON2_VERSION_13,
+ .allocate_cbk = nullptr, .free_cbk = nullptr,
+ .flags = ARGON2_DEFAULT_FLAGS
+ };
+ // libargon2 validates all the arguments so don't need to do it here
+ int const rc = argon2id_ctx(&context);
+ if (rc != ARGON2_OK)
+ {
+ SAL_WARN("package", "argon2id_ctx failed to derive key: " << argon2_error_message(rc));
+ throw ZipIOException("argon2id_ctx failed to derive key");
+ }
+ }
+ else if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
+ aDerivedKey.getLength(),
+ reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ),
+ xEncryptionData->m_aKey.getLength(),
+ reinterpret_cast< const sal_uInt8 * > ( xEncryptionData->m_aSalt.getConstArray() ),
+ xEncryptionData->m_aSalt.getLength(),
+ *xEncryptionData->m_oPBKDFIterationCount) )
+ {
+ throw ZipIOException("Can not create derived key!" );
+ }
+
+ if (xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING
+ || xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_GCM_W3C)
+ {
+ uno::Reference< uno::XComponentContext > xContext = xArgContext;
+ if ( !xContext.is() )
+ xContext = comphelper::getProcessComponentContext();
+
+ uno::Reference< xml::crypto::XNSSInitializer > xCipherContextSupplier = xml::crypto::NSSInitializer::create( xContext );
+
+ xResult = xCipherContextSupplier->getCipherContext( xEncryptionData->m_nEncAlg, aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt, uno::Sequence< beans::NamedValue >() );
+ }
+ else if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::BLOWFISH_CFB_8 )
+ {
+ xResult = BlowfishCFB8CipherContext::Create( aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt );
+ }
+ else
+ {
+ throw ZipIOException("Unknown cipher algorithm is requested!" );
+ }
+
+ return xResult;
+}
+
+void ZipFile::StaticFillHeader( const ::rtl::Reference< EncryptionData >& rData,
+ sal_Int64 nSize,
+ const OUString& aMediaType,
+ sal_Int8 * & pHeader )
+{
+ // I think it's safe to restrict vector and salt length to 2 bytes !
+ sal_Int16 nIVLength = static_cast < sal_Int16 > ( rData->m_aInitVector.getLength() );
+ sal_Int16 nSaltLength = static_cast < sal_Int16 > ( rData->m_aSalt.getLength() );
+ sal_Int16 nDigestLength = static_cast < sal_Int16 > ( rData->m_aDigest.getLength() );
+ sal_Int16 nMediaTypeLength = static_cast < sal_Int16 > ( aMediaType.getLength() * sizeof( sal_Unicode ) );
+
+ // First the header
+ *(pHeader++) = ( n_ConstHeader >> 0 ) & 0xFF;
+ *(pHeader++) = ( n_ConstHeader >> 8 ) & 0xFF;
+ *(pHeader++) = ( n_ConstHeader >> 16 ) & 0xFF;
+ *(pHeader++) = ( n_ConstHeader >> 24 ) & 0xFF;
+
+ // Then the version
+ *(pHeader++) = ( n_ConstCurrentVersion >> 0 ) & 0xFF;
+ *(pHeader++) = ( n_ConstCurrentVersion >> 8 ) & 0xFF;
+
+ // Then the iteration Count
+ sal_Int32 const nIterationCount = rData->m_oPBKDFIterationCount ? *rData->m_oPBKDFIterationCount : 0;
+ *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 24 ) & 0xFF);
+
+ sal_Int32 const nArgon2t = rData->m_oArgon2Args ? ::std::get<0>(*rData->m_oArgon2Args) : 0;
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 0) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 8) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 16) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 24) & 0xFF);
+
+ sal_Int32 const nArgon2m = rData->m_oArgon2Args ? ::std::get<1>(*rData->m_oArgon2Args) : 0;
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 0) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 8) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 16) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 24) & 0xFF);
+
+ sal_Int32 const nArgon2p = rData->m_oArgon2Args ? ::std::get<2>(*rData->m_oArgon2Args) : 0;
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 0) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 8) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 16) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 24) & 0xFF);
+
+ // FIXME64: need to handle larger sizes
+ // Then the size:
+ *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 24 ) & 0xFF);
+
+ // Then the encryption algorithm
+ sal_Int32 nEncAlgID = rData->m_nEncAlg;
+ *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 24 ) & 0xFF);
+
+ // Then the checksum algorithm
+ sal_Int32 nChecksumAlgID = rData->m_oCheckAlg ? *rData->m_oCheckAlg : 0;
+ *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 24 ) & 0xFF);
+
+ // Then the derived key size
+ sal_Int32 nDerivedKeySize = rData->m_nDerivedKeySize;
+ *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 24 ) & 0xFF);
+
+ // Then the start key generation algorithm
+ sal_Int32 nKeyAlgID = rData->m_nStartKeyGenID;
+ *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 24 ) & 0xFF);
+
+ // Then the salt length
+ *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 8 ) & 0xFF);
+
+ // Then the IV length
+ *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 8 ) & 0xFF);
+
+ // Then the digest length
+ *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 8 ) & 0xFF);
+
+ // Then the mediatype length
+ *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 8 ) & 0xFF);
+
+ // Then the salt content
+ memcpy ( pHeader, rData->m_aSalt.getConstArray(), nSaltLength );
+ pHeader += nSaltLength;
+
+ // Then the IV content
+ memcpy ( pHeader, rData->m_aInitVector.getConstArray(), nIVLength );
+ pHeader += nIVLength;
+
+ // Then the digest content
+ memcpy ( pHeader, rData->m_aDigest.getConstArray(), nDigestLength );
+ pHeader += nDigestLength;
+
+ // Then the mediatype itself
+ memcpy ( pHeader, aMediaType.getStr(), nMediaTypeLength );
+ pHeader += nMediaTypeLength;
+}
+
+bool ZipFile::StaticFillData ( ::rtl::Reference< BaseEncryptionData > const & rData,
+ sal_Int32 &rEncAlg,
+ sal_Int32 &rChecksumAlg,
+ sal_Int32 &rDerivedKeySize,
+ sal_Int32 &rStartKeyGenID,
+ sal_Int32 &rSize,
+ OUString& aMediaType,
+ const uno::Reference< XInputStream >& rStream )
+{
+ bool bOk = false;
+ const sal_Int32 nHeaderSize = n_ConstHeaderSize - 4;
+ Sequence < sal_Int8 > aBuffer ( nHeaderSize );
+ if ( nHeaderSize == rStream->readBytes ( aBuffer, nHeaderSize ) )
+ {
+ sal_Int16 nPos = 0;
+ sal_Int8 *pBuffer = aBuffer.getArray();
+ sal_Int16 nVersion = pBuffer[nPos++] & 0xFF;
+ nVersion |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ if ( nVersion == n_ConstCurrentVersion )
+ {
+ sal_Int32 nCount = pBuffer[nPos++] & 0xFF;
+ nCount |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ nCount |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ nCount |= ( pBuffer[nPos++] & 0xFF ) << 24;
+ if (nCount != 0)
+ {
+ rData->m_oPBKDFIterationCount.emplace(nCount);
+ }
+ else
+ {
+ rData->m_oPBKDFIterationCount.reset();
+ }
+
+ sal_Int32 nArgon2t = pBuffer[nPos++] & 0xFF;
+ nArgon2t |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ nArgon2t |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ nArgon2t |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ sal_Int32 nArgon2m = pBuffer[nPos++] & 0xFF;
+ nArgon2m |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ nArgon2m |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ nArgon2m |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ sal_Int32 nArgon2p = pBuffer[nPos++] & 0xFF;
+ nArgon2p |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ nArgon2p |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ nArgon2p |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ if (nArgon2t != 0 && nArgon2m != 0 && nArgon2p != 0)
+ {
+ rData->m_oArgon2Args.emplace(nArgon2t, nArgon2m, nArgon2p);
+ }
+ else
+ {
+ rData->m_oArgon2Args.reset();
+ }
+
+ rSize = pBuffer[nPos++] & 0xFF;
+ rSize |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rSize |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rSize |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ rEncAlg = pBuffer[nPos++] & 0xFF;
+ rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ rChecksumAlg = pBuffer[nPos++] & 0xFF;
+ rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ rDerivedKeySize = pBuffer[nPos++] & 0xFF;
+ rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ rStartKeyGenID = pBuffer[nPos++] & 0xFF;
+ rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ sal_Int16 nSaltLength = pBuffer[nPos++] & 0xFF;
+ nSaltLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ sal_Int16 nIVLength = ( pBuffer[nPos++] & 0xFF );
+ nIVLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ sal_Int16 nDigestLength = pBuffer[nPos++] & 0xFF;
+ nDigestLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
+
+ sal_Int16 nMediaTypeLength = pBuffer[nPos++] & 0xFF;
+ nMediaTypeLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
+
+ if ( nSaltLength == rStream->readBytes ( aBuffer, nSaltLength ) )
+ {
+ rData->m_aSalt.realloc ( nSaltLength );
+ memcpy ( rData->m_aSalt.getArray(), aBuffer.getConstArray(), nSaltLength );
+ if ( nIVLength == rStream->readBytes ( aBuffer, nIVLength ) )
+ {
+ rData->m_aInitVector.realloc ( nIVLength );
+ memcpy ( rData->m_aInitVector.getArray(), aBuffer.getConstArray(), nIVLength );
+ if ( nDigestLength == rStream->readBytes ( aBuffer, nDigestLength ) )
+ {
+ rData->m_aDigest.realloc ( nDigestLength );
+ memcpy ( rData->m_aDigest.getArray(), aBuffer.getConstArray(), nDigestLength );
+
+ if ( nMediaTypeLength == rStream->readBytes ( aBuffer, nMediaTypeLength ) )
+ {
+ aMediaType = OUString( reinterpret_cast<sal_Unicode const *>(aBuffer.getConstArray()),
+ nMediaTypeLength / sizeof( sal_Unicode ) );
+ bOk = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return bOk;
+}
+
+#if 0
+// for debugging purposes
+void CheckSequence( const uno::Sequence< sal_Int8 >& aSequence )
+{
+ if ( aSequence.getLength() )
+ {
+ sal_Int32* pPointer = *( (sal_Int32**)&aSequence );
+ sal_Int32 nSize = *( pPointer + 1 );
+ sal_Int32 nMemSize = *( pPointer - 2 );
+ sal_Int32 nUsedMemSize = ( nSize + 4 * sizeof( sal_Int32 ) );
+ OSL_ENSURE( nSize == aSequence.getLength() && nUsedMemSize + 7 - ( nUsedMemSize - 1 ) % 8 == nMemSize, "Broken Sequence!" );
+ }
+}
+#endif
+
+bool ZipFile::StaticHasValidPassword( const uno::Reference< uno::XComponentContext >& rxContext, const Sequence< sal_Int8 > &aReadBuffer, const ::rtl::Reference< EncryptionData > &rData )
+{
+ assert(rData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C); // should not be called for AEAD
+
+ if ( !rData.is() || !rData->m_aKey.hasElements() )
+ return false;
+
+ bool bRet = false;
+
+ uno::Reference< xml::crypto::XCipherContext > xCipher( StaticGetCipher( rxContext, rData, false ), uno::UNO_SET_THROW );
+
+ uno::Sequence< sal_Int8 > aDecryptBuffer;
+ uno::Sequence< sal_Int8 > aDecryptBuffer2;
+ try
+ {
+ aDecryptBuffer = xCipher->convertWithCipherContext( aReadBuffer );
+ aDecryptBuffer2 = xCipher->finalizeCipherContextAndDispose();
+ }
+ catch( uno::Exception& )
+ {
+ // decryption with padding will throw the exception in finalizing if the buffer represent only part of the stream
+ // it is no problem, actually this is why we read 32 additional bytes ( two of maximal possible encryption blocks )
+ }
+
+ if ( aDecryptBuffer2.hasElements() )
+ {
+ sal_Int32 nOldLen = aDecryptBuffer.getLength();
+ aDecryptBuffer.realloc( nOldLen + aDecryptBuffer2.getLength() );
+ memcpy( aDecryptBuffer.getArray() + nOldLen, aDecryptBuffer2.getConstArray(), aDecryptBuffer2.getLength() );
+ }
+
+ if ( aDecryptBuffer.getLength() > n_ConstDigestLength )
+ aDecryptBuffer.realloc( n_ConstDigestLength );
+
+ uno::Sequence< sal_Int8 > aDigestSeq;
+ uno::Reference< xml::crypto::XDigestContext > xDigestContext( StaticGetDigestContextForChecksum( rxContext, rData ), uno::UNO_SET_THROW );
+
+ xDigestContext->updateDigest( aDecryptBuffer );
+ aDigestSeq = xDigestContext->finalizeDigestAndDispose();
+
+ // If we don't have a digest, then we have to assume that the password is correct
+ if ( rData->m_aDigest.hasElements() &&
+ ( aDigestSeq.getLength() != rData->m_aDigest.getLength() ||
+ 0 != memcmp ( aDigestSeq.getConstArray(),
+ rData->m_aDigest.getConstArray(),
+ aDigestSeq.getLength() ) ) )
+ {
+ // We should probably tell the user that the password they entered was wrong
+ }
+ else
+ bRet = true;
+
+ return bRet;
+}
+
+uno::Reference<io::XInputStream> ZipFile::checkValidPassword(
+ ZipEntry const& rEntry, ::rtl::Reference<EncryptionData> const& rData,
+ rtl::Reference<comphelper::RefCountedMutex> const& rMutex)
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if (rData.is() && rData->m_nEncAlg == xml::crypto::CipherID::AES_GCM_W3C)
+ {
+ try // the only way to find out: decrypt the whole stream, which will
+ { // check the tag
+ uno::Reference<io::XInputStream> const xRet =
+ createStreamForZipEntry(rMutex, rEntry, rData, UNBUFF_STREAM_DATA, true);
+ // currently XBufferedStream reads the whole stream in its ctor (to
+ // verify the tag) - in case this gets changed, explicitly seek here
+ uno::Reference<io::XSeekable> const xSeek(xRet, uno::UNO_QUERY_THROW);
+ xSeek->seek(xSeek->getLength());
+ xSeek->seek(0);
+ return xRet;
+ }
+ catch (uno::Exception const&)
+ {
+ return {};
+ }
+ }
+ else if (rData.is() && rData->m_aKey.hasElements())
+ {
+ css::uno::Reference < css::io::XSeekable > xSeek(xStream, UNO_QUERY_THROW);
+ xSeek->seek( rEntry.nOffset );
+ sal_Int64 nSize = rEntry.nMethod == DEFLATED ? rEntry.nCompressedSize : rEntry.nSize;
+
+ // Only want to read enough to verify the digest
+ if ( nSize > n_ConstDigestDecrypt )
+ nSize = n_ConstDigestDecrypt;
+
+ Sequence < sal_Int8 > aReadBuffer ( nSize );
+
+ xStream->readBytes( aReadBuffer, nSize );
+
+ if (StaticHasValidPassword(m_xContext, aReadBuffer, rData))
+ {
+ return createStreamForZipEntry(
+ rMutex, rEntry, rData, UNBUFF_STREAM_DATA, true);
+ }
+ }
+
+ return {};
+}
+
+namespace {
+
+class XBufferedStream : public cppu::WeakImplHelper<css::io::XInputStream, css::io::XSeekable>
+{
+ std::vector<sal_Int8> maBytes;
+ size_t mnPos;
+
+ size_t remainingSize() const
+ {
+ return maBytes.size() - mnPos;
+ }
+
+ bool hasBytes() const
+ {
+ return mnPos < maBytes.size();
+ }
+
+public:
+ XBufferedStream( const uno::Reference<XInputStream>& xSrcStream ) : mnPos(0)
+ {
+ sal_Int32 nRemaining = xSrcStream->available();
+ maBytes.reserve(nRemaining);
+
+ if (auto pByteReader = dynamic_cast< comphelper::ByteReader* >( xSrcStream.get() ))
+ {
+ maBytes.resize(nRemaining);
+
+ sal_Int8* pData = maBytes.data();
+ while (nRemaining > 0)
+ {
+ sal_Int32 nRead = pByteReader->readSomeBytes(pData, nRemaining);
+ nRemaining -= nRead;
+ pData += nRead;
+ }
+ return;
+ }
+
+ const sal_Int32 nBufSize = 8192;
+ uno::Sequence<sal_Int8> aBuf(nBufSize);
+ while (nRemaining > 0)
+ {
+ const sal_Int32 nBytes = xSrcStream->readBytes(aBuf, std::min(nBufSize, nRemaining));
+ if (!nBytes)
+ break;
+ maBytes.insert(maBytes.end(), aBuf.begin(), aBuf.begin() + nBytes);
+ nRemaining -= nBytes;
+ }
+ }
+
+ virtual sal_Int32 SAL_CALL readBytes( uno::Sequence<sal_Int8>& rData, sal_Int32 nBytesToRead ) override
+ {
+ if (!hasBytes())
+ return 0;
+
+ sal_Int32 nReadSize = std::min<sal_Int32>(nBytesToRead, remainingSize());
+ rData.realloc(nReadSize);
+ auto pData = rData.getArray();
+ std::vector<sal_Int8>::const_iterator it = maBytes.cbegin();
+ std::advance(it, mnPos);
+ for (sal_Int32 i = 0; i < nReadSize; ++i, ++it)
+ pData[i] = *it;
+
+ mnPos += nReadSize;
+
+ return nReadSize;
+ }
+
+ virtual sal_Int32 SAL_CALL readSomeBytes( ::css::uno::Sequence<sal_Int8>& rData, sal_Int32 nMaxBytesToRead ) override
+ {
+ return readBytes(rData, nMaxBytesToRead);
+ }
+
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override
+ {
+ if (!hasBytes())
+ return;
+
+ mnPos += nBytesToSkip;
+ }
+
+ virtual sal_Int32 SAL_CALL available() override
+ {
+ if (!hasBytes())
+ return 0;
+
+ return remainingSize();
+ }
+
+ virtual void SAL_CALL closeInput() override
+ {
+ }
+ // XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override
+ {
+ if ( location < 0 || o3tl::make_unsigned(location) > maBytes.size() )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+ mnPos = location;
+ }
+ virtual sal_Int64 SAL_CALL getPosition() override
+ {
+ return mnPos;
+ }
+ virtual sal_Int64 SAL_CALL getLength() override
+ {
+ return maBytes.size();
+ }
+};
+
+}
+
+uno::Reference< XInputStream > ZipFile::createStreamForZipEntry(
+ const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
+ ZipEntry const & rEntry,
+ const ::rtl::Reference< EncryptionData > &rData,
+ sal_Int8 nStreamMode,
+ bool bIsEncrypted,
+ const bool bUseBufferedStream,
+ const OUString& aMediaType )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ rtl::Reference< XUnbufferedStream > xSrcStream = new XUnbufferedStream(
+ m_xContext, aMutexHolder, rEntry, xStream, rData, nStreamMode, bIsEncrypted, aMediaType, bRecoveryMode);
+
+ if (!bUseBufferedStream)
+ return xSrcStream;
+
+ uno::Reference<io::XInputStream> xBufStream;
+#ifndef EMSCRIPTEN
+ static const sal_Int32 nThreadingThreshold = 10000;
+
+ // "encrypted-package" is the only data stream, no point in threading it
+ if (rEntry.sPath != "encrypted-package" && nThreadingThreshold < xSrcStream->available())
+ xBufStream = new XBufferedThreadedStream(xSrcStream, xSrcStream->getSize());
+ else
+#endif
+ xBufStream = new XBufferedStream(xSrcStream);
+
+ return xBufStream;
+}
+
+uno::Reference< XInputStream > ZipFile::StaticGetDataFromRawStream(
+ const rtl::Reference<comphelper::RefCountedMutex>& rMutexHolder,
+ const uno::Reference<uno::XComponentContext>& rxContext,
+ const uno::Reference<XInputStream>& xStream,
+ const ::rtl::Reference<EncryptionData> &rData)
+{
+ if (!rData.is())
+ throw ZipIOException("Encrypted stream without encryption data!" );
+
+ if (!rData->m_aKey.hasElements())
+ throw packages::WrongPasswordException(THROW_WHERE);
+
+ uno::Reference<XSeekable> xSeek(xStream, UNO_QUERY);
+ if (!xSeek.is())
+ throw ZipIOException("The stream must be seekable!");
+
+ // if we have a digest, then this file is an encrypted one and we should
+ // check if we can decrypt it or not
+ SAL_WARN_IF(rData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C && !rData->m_aDigest.hasElements(),
+ "package", "Can't detect password correctness without digest!");
+ if (rData->m_nEncAlg == xml::crypto::CipherID::AES_GCM_W3C)
+ {
+ // skip header
+ xSeek->seek(n_ConstHeaderSize + rData->m_aInitVector.getLength()
+ + rData->m_aSalt.getLength() + rData->m_aDigest.getLength());
+
+ try
+ { // XUnbufferedStream does not support XSeekable so wrap it
+ ::rtl::Reference<XBufferedStream> const pRet(
+ new XBufferedStream(new XUnbufferedStream(rMutexHolder, xStream, rData)));
+ // currently XBufferedStream reads the whole stream in its ctor (to
+ // verify the tag) - in case this gets changed, explicitly seek here
+ pRet->seek(pRet->getLength());
+ pRet->seek(0);
+ return pRet;
+ }
+ catch (uno::Exception const&)
+ {
+ throw packages::WrongPasswordException(THROW_WHERE);
+ }
+ }
+ else if (rData->m_aDigest.hasElements())
+ {
+ sal_Int32 nSize = sal::static_int_cast<sal_Int32>(xSeek->getLength());
+ if (nSize > n_ConstDigestLength + 32)
+ nSize = n_ConstDigestLength + 32;
+
+ // skip header
+ xSeek->seek(n_ConstHeaderSize + rData->m_aInitVector.getLength() +
+ rData->m_aSalt.getLength() + rData->m_aDigest.getLength());
+
+ // Only want to read enough to verify the digest
+ Sequence<sal_Int8> aReadBuffer(nSize);
+
+ xStream->readBytes(aReadBuffer, nSize);
+
+ if (!StaticHasValidPassword(rxContext, aReadBuffer, rData))
+ throw packages::WrongPasswordException(THROW_WHERE);
+ }
+
+ return new XUnbufferedStream(rMutexHolder, xStream, rData);
+}
+
+ZipEnumeration ZipFile::entries()
+{
+ return aEntries;
+}
+
+uno::Reference< XInputStream > ZipFile::getInputStream( ZipEntry& rEntry,
+ const ::rtl::Reference< EncryptionData > &rData,
+ bool bIsEncrypted,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( rEntry.nOffset <= 0 )
+ readLOC( rEntry );
+
+ // We want to return a rawStream if we either don't have a key or if the
+ // key is wrong
+
+ bool bNeedRawStream = rEntry.nMethod == STORED;
+
+ if (bIsEncrypted && rData.is())
+ {
+ uno::Reference<XInputStream> const xRet(checkValidPassword(rEntry, rData, aMutexHolder));
+ if (xRet.is())
+ {
+ return xRet;
+ }
+ bNeedRawStream = true;
+ }
+
+ return createStreamForZipEntry ( aMutexHolder,
+ rEntry,
+ rData,
+ bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
+ bIsEncrypted );
+}
+
+uno::Reference< XInputStream > ZipFile::getDataStream( ZipEntry& rEntry,
+ const ::rtl::Reference< EncryptionData > &rData,
+ bool bIsEncrypted,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( rEntry.nOffset <= 0 )
+ readLOC( rEntry );
+
+ // An exception must be thrown in case stream is encrypted and
+ // there is no key or the key is wrong
+ bool bNeedRawStream = false;
+ if ( bIsEncrypted )
+ {
+ // in case no digest is provided there is no way
+ // to detect password correctness
+ if ( !rData.is() )
+ throw ZipException("Encrypted stream without encryption data!" );
+
+ // if we have a digest, then this file is an encrypted one and we should
+ // check if we can decrypt it or not
+ SAL_WARN_IF(rData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C && !rData->m_aDigest.hasElements(),
+ "package", "Can't detect password correctness without digest!");
+ uno::Reference<XInputStream> const xRet(checkValidPassword(rEntry, rData, aMutexHolder));
+ if (!xRet.is())
+ {
+ throw packages::WrongPasswordException(THROW_WHERE);
+ }
+ return xRet;
+ }
+ else
+ bNeedRawStream = ( rEntry.nMethod == STORED );
+
+ return createStreamForZipEntry ( aMutexHolder,
+ rEntry,
+ rData,
+ bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
+ bIsEncrypted );
+}
+
+uno::Reference< XInputStream > ZipFile::getRawData( ZipEntry& rEntry,
+ const ::rtl::Reference< EncryptionData >& rData,
+ bool bIsEncrypted,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
+ const bool bUseBufferedStream )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( rEntry.nOffset <= 0 )
+ readLOC( rEntry );
+
+ return createStreamForZipEntry ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_RAW, bIsEncrypted, bUseBufferedStream );
+}
+
+uno::Reference< XInputStream > ZipFile::getWrappedRawStream(
+ ZipEntry& rEntry,
+ const ::rtl::Reference< EncryptionData >& rData,
+ const OUString& aMediaType,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( !rData.is() )
+ throw packages::NoEncryptionException(THROW_WHERE );
+
+ if ( rEntry.nOffset <= 0 )
+ readLOC( rEntry );
+
+ return createStreamForZipEntry ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_WRAPPEDRAW, true, true, aMediaType );
+}
+
+void ZipFile::readLOC( ZipEntry &rEntry )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ sal_Int64 nPos = -rEntry.nOffset;
+
+ aGrabber.seek(nPos);
+ sal_Int32 nTestSig = aGrabber.ReadInt32();
+ if (nTestSig != LOCSIG)
+ throw ZipIOException("Invalid LOC header (bad signature)" );
+
+ // Ignore all (duplicated) information from the local file header.
+ // various programs produced "broken" zip files; even LO at some point.
+ // Just verify the path and calculate the data offset and otherwise
+ // rely on the central directory info.
+
+ aGrabber.ReadInt16(); //version
+ aGrabber.ReadInt16(); //flag
+ aGrabber.ReadInt16(); //how
+ aGrabber.ReadInt32(); //time
+ aGrabber.ReadInt32(); //crc
+ aGrabber.ReadInt32(); //compressed size
+ aGrabber.ReadInt32(); //size
+ sal_Int16 nPathLen = aGrabber.ReadInt16();
+ sal_Int16 nExtraLen = aGrabber.ReadInt16();
+
+ if (nPathLen < 0)
+ {
+ SAL_WARN("package", "bogus path len of: " << nPathLen);
+ nPathLen = 0;
+ }
+
+ rEntry.nOffset = aGrabber.getPosition() + nPathLen + nExtraLen;
+
+ bool bBroken = false;
+
+ try
+ {
+ // read always in UTF8, some tools seem not to set UTF8 bit
+ // coverity[tainted_data] - we've checked negative lens, and up to max short is ok here
+ uno::Sequence<sal_Int8> aNameBuffer(nPathLen);
+ sal_Int32 nRead = aGrabber.readBytes(aNameBuffer, nPathLen);
+ if (nRead < aNameBuffer.getLength())
+ aNameBuffer.realloc(nRead);
+
+ OUString sLOCPath( reinterpret_cast<const char *>(aNameBuffer.getConstArray()),
+ aNameBuffer.getLength(),
+ RTL_TEXTENCODING_UTF8 );
+
+ if ( rEntry.nPathLen == -1 ) // the file was created
+ {
+ rEntry.nPathLen = nPathLen;
+ rEntry.sPath = sLOCPath;
+ }
+
+ bBroken = rEntry.nPathLen != nPathLen
+ || rEntry.sPath != sLOCPath;
+ }
+ catch(...)
+ {
+ bBroken = true;
+ }
+
+ if ( bBroken && !bRecoveryMode )
+ throw ZipIOException("The stream seems to be broken!" );
+}
+
+sal_Int32 ZipFile::findEND()
+{
+ // this method is called in constructor only, no need for mutex
+ sal_Int32 nPos, nEnd;
+ Sequence < sal_Int8 > aBuffer;
+ try
+ {
+ sal_Int32 nLength = static_cast <sal_Int32 > (aGrabber.getLength());
+ if (nLength < ENDHDR)
+ return -1;
+ nPos = nLength - ENDHDR - ZIP_MAXNAMELEN;
+ nEnd = nPos >= 0 ? nPos : 0 ;
+
+ aGrabber.seek( nEnd );
+
+ auto nSize = nLength - nEnd;
+ if (nSize != aGrabber.readBytes(aBuffer, nSize))
+ throw ZipException("Zip END signature not found!" );
+
+ const sal_Int8 *pBuffer = aBuffer.getConstArray();
+
+ nPos = nSize - ENDHDR;
+ while ( nPos >= 0 )
+ {
+ if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 5 && pBuffer[nPos+3] == 6 )
+ return nPos + nEnd;
+ nPos--;
+ }
+ }
+ catch ( IllegalArgumentException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ catch ( NotConnectedException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ catch ( BufferSizeExceededException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ throw ZipException("Zip END signature not found!" );
+}
+
+sal_Int32 ZipFile::readCEN()
+{
+ // this method is called in constructor only, no need for mutex
+ sal_Int32 nCenPos = -1, nLocPos;
+ sal_uInt16 nCount;
+
+ try
+ {
+ sal_Int32 nEndPos = findEND();
+ if (nEndPos == -1)
+ return -1;
+ aGrabber.seek(nEndPos + ENDTOT);
+ sal_uInt16 nTotal = aGrabber.ReadUInt16();
+ sal_Int32 nCenLen = aGrabber.ReadInt32();
+ sal_Int32 nCenOff = aGrabber.ReadInt32();
+
+ if ( nTotal * CENHDR > nCenLen )
+ throw ZipException("invalid END header (bad entry count)" );
+
+ if ( nTotal > ZIP_MAXENTRIES )
+ throw ZipException("too many entries in ZIP File" );
+
+ if ( nCenLen < 0 || nCenLen > nEndPos )
+ throw ZipException("Invalid END header (bad central directory size)" );
+
+ nCenPos = nEndPos - nCenLen;
+
+ if ( nCenOff < 0 || nCenOff > nCenPos )
+ throw ZipException("Invalid END header (bad central directory size)" );
+
+ nLocPos = nCenPos - nCenOff;
+ aGrabber.seek( nCenPos );
+ Sequence < sal_Int8 > aCENBuffer ( nCenLen );
+ sal_Int64 nRead = aGrabber.readBytes ( aCENBuffer, nCenLen );
+ if ( static_cast < sal_Int64 > ( nCenLen ) != nRead )
+ throw ZipException ("Error reading CEN into memory buffer!" );
+
+ MemoryByteGrabber aMemGrabber(aCENBuffer);
+
+ ZipEntry aEntry;
+ sal_Int16 nCommentLen;
+
+ aEntries.reserve(nTotal);
+ for (nCount = 0 ; nCount < nTotal; nCount++)
+ {
+ sal_Int32 nTestSig = aMemGrabber.ReadInt32();
+ if ( nTestSig != CENSIG )
+ throw ZipException("Invalid CEN header (bad signature)" );
+
+ aMemGrabber.skipBytes ( 2 );
+ aEntry.nVersion = aMemGrabber.ReadInt16();
+ aEntry.nFlag = aMemGrabber.ReadInt16();
+
+ if ( ( aEntry.nFlag & 1 ) == 1 )
+ throw ZipException("Invalid CEN header (encrypted entry)" );
+
+ aEntry.nMethod = aMemGrabber.ReadInt16();
+
+ if ( aEntry.nMethod != STORED && aEntry.nMethod != DEFLATED)
+ throw ZipException("Invalid CEN header (bad compression method)" );
+
+ aEntry.nTime = aMemGrabber.ReadInt32();
+ aEntry.nCrc = aMemGrabber.ReadInt32();
+
+ sal_uInt64 nCompressedSize = aMemGrabber.ReadUInt32();
+ sal_uInt64 nSize = aMemGrabber.ReadUInt32();
+ aEntry.nPathLen = aMemGrabber.ReadInt16();
+ aEntry.nExtraLen = aMemGrabber.ReadInt16();
+ nCommentLen = aMemGrabber.ReadInt16();
+ aMemGrabber.skipBytes ( 8 );
+ sal_uInt64 nOffset = aMemGrabber.ReadUInt32();
+
+ if ( aEntry.nPathLen < 0 )
+ throw ZipException("unexpected name length" );
+
+ if ( nCommentLen < 0 )
+ throw ZipException("unexpected comment length" );
+
+ if ( aEntry.nExtraLen < 0 )
+ throw ZipException("unexpected extra header info length" );
+
+ if (aEntry.nPathLen > aMemGrabber.remainingSize())
+ throw ZipException("name too long");
+
+ // read always in UTF8, some tools seem not to set UTF8 bit
+ aEntry.sPath = OUString( reinterpret_cast<char const *>(aMemGrabber.getCurrentPos()),
+ aEntry.nPathLen,
+ RTL_TEXTENCODING_UTF8 );
+
+ if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aEntry.sPath, true ) )
+ throw ZipException("Zip entry has an invalid name." );
+
+ aMemGrabber.skipBytes(aEntry.nPathLen);
+
+ if (aEntry.nExtraLen>0)
+ {
+ readExtraFields(aMemGrabber, aEntry.nExtraLen, nSize, nCompressedSize, &nOffset);
+ }
+ aEntry.nCompressedSize = nCompressedSize;
+ aEntry.nSize = nSize;
+ aEntry.nOffset = nOffset;
+
+ if (o3tl::checked_add<sal_Int64>(aEntry.nOffset, nLocPos, aEntry.nOffset))
+ throw ZipException("Integer-overflow");
+ if (o3tl::checked_multiply<sal_Int64>(aEntry.nOffset, -1, aEntry.nOffset))
+ throw ZipException("Integer-overflow");
+
+ aMemGrabber.skipBytes(nCommentLen);
+ aEntries[aEntry.sPath] = aEntry;
+ }
+
+ if (nCount != nTotal)
+ throw ZipException("Count != Total" );
+ }
+ catch ( IllegalArgumentException & )
+ {
+ // seek can throw this...
+ nCenPos = -1; // make sure we return -1 to indicate an error
+ }
+ return nCenPos;
+}
+
+void ZipFile::readExtraFields(MemoryByteGrabber& aMemGrabber, sal_Int16 nExtraLen,
+ sal_uInt64& nSize, sal_uInt64& nCompressedSize, sal_uInt64* nOffset)
+{
+ while (nExtraLen > 0) // Extensible data fields
+ {
+ sal_Int16 nheaderID = aMemGrabber.ReadInt16();
+ sal_uInt16 dataSize = aMemGrabber.ReadUInt16();
+ if (nheaderID == 1) // Load Zip64 Extended Information Extra Field
+ {
+ // Datasize should be 28byte but some files have less (maybe non standard?)
+ nSize = aMemGrabber.ReadUInt64();
+ sal_uInt16 nReadSize = 8;
+ if (dataSize >= 16)
+ {
+ nCompressedSize = aMemGrabber.ReadUInt64();
+ nReadSize = 16;
+ if (dataSize >= 24 && nOffset)
+ {
+ *nOffset = aMemGrabber.ReadUInt64();
+ nReadSize = 24;
+ // 4 byte should be "Disk Start Number" but we not need it
+ }
+ }
+ if (dataSize > nReadSize)
+ aMemGrabber.skipBytes(dataSize - nReadSize);
+ }
+ else
+ {
+ aMemGrabber.skipBytes(dataSize);
+ }
+ nExtraLen -= dataSize + 4;
+ }
+}
+
+void ZipFile::recover()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ sal_Int64 nLength;
+ Sequence < sal_Int8 > aBuffer;
+
+ try
+ {
+ nLength = aGrabber.getLength();
+ if (nLength < ENDHDR)
+ return;
+
+ aGrabber.seek( 0 );
+
+ const sal_Int64 nToRead = 32000;
+ for( sal_Int64 nGenPos = 0; aGrabber.readBytes( aBuffer, nToRead ) && aBuffer.getLength() > 16; )
+ {
+ const sal_Int8 *pBuffer = aBuffer.getConstArray();
+ sal_Int32 nBufSize = aBuffer.getLength();
+
+ sal_Int64 nPos = 0;
+ // the buffer should contain at least one header,
+ // or if it is end of the file, at least the postheader with sizes and hash
+ while( nPos < nBufSize - 30
+ || ( nBufSize < nToRead && nPos < nBufSize - 16 ) )
+
+ {
+ if ( nPos < nBufSize - 30 && pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4 )
+ {
+ //PK34: Local file header
+ ZipEntry aEntry;
+ Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos+4]), 26);
+ MemoryByteGrabber aMemGrabber(aTmpBuffer);
+
+ aEntry.nVersion = aMemGrabber.ReadInt16();
+ aEntry.nFlag = aMemGrabber.ReadInt16();
+
+ if ( ( aEntry.nFlag & 1 ) != 1 )
+ {
+ aEntry.nMethod = aMemGrabber.ReadInt16();
+
+ if ( aEntry.nMethod == STORED || aEntry.nMethod == DEFLATED )
+ {
+ aEntry.nTime = aMemGrabber.ReadInt32();
+ aEntry.nCrc = aMemGrabber.ReadInt32();
+ sal_uInt64 nCompressedSize = aMemGrabber.ReadUInt32();
+ sal_uInt64 nSize = aMemGrabber.ReadUInt32();
+ aEntry.nPathLen = aMemGrabber.ReadInt16();
+ aEntry.nExtraLen = aMemGrabber.ReadInt16();
+
+ sal_Int32 nDescrLength =
+ ( aEntry.nMethod == DEFLATED && ( aEntry.nFlag & 8 ) ) ? 16 : 0;
+
+ sal_Int64 nBlockHeaderLength = aEntry.nPathLen + aEntry.nExtraLen + 30 + nDescrLength;
+ if ( aEntry.nPathLen >= 0 && aEntry.nExtraLen >= 0
+ && ( nGenPos + nPos + nBlockHeaderLength ) <= nLength )
+ {
+ // read always in UTF8, some tools seem not to set UTF8 bit
+ if( nPos + 30 + aEntry.nPathLen <= nBufSize )
+ aEntry.sPath = OUString ( reinterpret_cast<char const *>(&pBuffer[nPos + 30]),
+ aEntry.nPathLen,
+ RTL_TEXTENCODING_UTF8 );
+ else
+ {
+ Sequence < sal_Int8 > aFileName;
+ aGrabber.seek( nGenPos + nPos + 30 );
+ aGrabber.readBytes( aFileName, aEntry.nPathLen );
+ aEntry.sPath = OUString ( reinterpret_cast<const char *>(aFileName.getConstArray()),
+ aFileName.getLength(),
+ RTL_TEXTENCODING_UTF8 );
+ aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength());
+ }
+
+ // read 64bit header
+ if (aEntry.nExtraLen > 0)
+ {
+ Sequence<sal_Int8> aExtraBuffer;
+ if (nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen <= nBufSize)
+ {
+ aExtraBuffer = Sequence<sal_Int8>(
+ &(pBuffer[nPos + 30 + aEntry.nPathLen]),
+ aEntry.nExtraLen);
+ }
+ else
+ {
+ aGrabber.seek(nGenPos + nPos + 30 + aEntry.nExtraLen);
+ aGrabber.readBytes(aExtraBuffer, aEntry.nExtraLen);
+ }
+ MemoryByteGrabber aMemGrabberExtra(aExtraBuffer);
+ if (aEntry.nExtraLen > 0)
+ {
+ readExtraFields(aMemGrabberExtra, aEntry.nExtraLen, nSize,
+ nCompressedSize, nullptr);
+ }
+ }
+
+ sal_Int64 nDataSize = ( aEntry.nMethod == DEFLATED ) ? nCompressedSize : nSize;
+ sal_Int64 nBlockLength = nDataSize + nBlockHeaderLength;
+
+ if (( nGenPos + nPos + nBlockLength ) <= nLength )
+ {
+ aEntry.nCompressedSize = nCompressedSize;
+ aEntry.nSize = nSize;
+
+ aEntry.nOffset = nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen;
+
+ if ( ( aEntry.nSize || aEntry.nCompressedSize ) && !checkSizeAndCRC( aEntry ) )
+ {
+ aEntry.nCrc = 0;
+ aEntry.nCompressedSize = 0;
+ aEntry.nSize = 0;
+ }
+
+ aEntries.emplace( aEntry.sPath, aEntry );
+ }
+ }
+ }
+ }
+
+ nPos += 4;
+ }
+ else if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8 )
+ {
+ //PK78: Data descriptor
+ sal_Int64 nCompressedSize, nSize;
+ Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos + 4]), 12 + 8 + 4);
+ MemoryByteGrabber aMemGrabber(aTmpBuffer);
+ sal_Int32 nCRC32 = aMemGrabber.ReadInt32();
+
+ // FIXME64: find a better way to recognize if Zip64 mode is used
+ // Now we check if the memory at +16 byte seems to be a signature
+ // if not, then probably Zip64 mode is used here, except
+ // if memory at +24 byte seems not to be a signature.
+ // Normally Data Descriptor should followed by the next Local File header
+ // that should start with PK34, except for the last file, then it may
+ // followed by Central directory that start with PK12, or
+ // followed by "archive decryption header" that don't have a signature.
+ if ((pBuffer[nPos + 16] == 'P' && pBuffer[nPos + 17] == 'K'
+ && pBuffer[nPos + 19] == pBuffer[nPos + 18] + 1
+ && (pBuffer[nPos + 18] == 3 || pBuffer[nPos + 18] == 1))
+ || !(pBuffer[nPos + 24] == 'P' && pBuffer[nPos + 25] == 'K'
+ && pBuffer[nPos + 27] == pBuffer[nPos + 26] + 1
+ && (pBuffer[nPos + 26] == 3 || pBuffer[nPos + 26] == 1)))
+ {
+ nCompressedSize = aMemGrabber.ReadUInt32();
+ nSize = aMemGrabber.ReadUInt32();
+ }
+ else
+ {
+ nCompressedSize = aMemGrabber.ReadUInt64();
+ nSize = aMemGrabber.ReadUInt64();
+ }
+
+ for( auto& rEntry : aEntries )
+ {
+ // this is a broken package, accept this block not only for DEFLATED streams
+ if( rEntry.second.nFlag & 8 )
+ {
+ sal_Int64 nStreamOffset = nGenPos + nPos - nCompressedSize;
+ if ( nStreamOffset == rEntry.second.nOffset && nCompressedSize > rEntry.second.nCompressedSize )
+ {
+ // only DEFLATED blocks need to be checked
+ bool bAcceptBlock = ( rEntry.second.nMethod == STORED && nCompressedSize == nSize );
+
+ if ( !bAcceptBlock )
+ {
+ sal_Int64 nRealSize = 0;
+ sal_Int32 nRealCRC = 0;
+ getSizeAndCRC( nStreamOffset, nCompressedSize, &nRealSize, &nRealCRC );
+ bAcceptBlock = ( nRealSize == nSize && nRealCRC == nCRC32 );
+ }
+
+ if ( bAcceptBlock )
+ {
+ rEntry.second.nCrc = nCRC32;
+ rEntry.second.nCompressedSize = nCompressedSize;
+ rEntry.second.nSize = nSize;
+ }
+ }
+#if 0
+// for now ignore clearly broken streams
+ else if( !rEntry.second.nCompressedSize )
+ {
+ rEntry.second.nCrc = nCRC32;
+ sal_Int32 nRealStreamSize = nGenPos + nPos - rEntry.second.nOffset;
+ rEntry.second.nCompressedSize = nRealStreamSize;
+ rEntry.second.nSize = nSize;
+ }
+#endif
+ }
+ }
+
+ nPos += 4;
+ }
+ else
+ nPos++;
+ }
+
+ nGenPos += nPos;
+ aGrabber.seek( nGenPos );
+ }
+ }
+ catch ( IllegalArgumentException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ catch ( NotConnectedException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ catch ( BufferSizeExceededException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+}
+
+bool ZipFile::checkSizeAndCRC( const ZipEntry& aEntry )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ sal_Int32 nCRC = 0;
+ sal_Int64 nSize = 0;
+
+ if( aEntry.nMethod == STORED )
+ return ( getCRC( aEntry.nOffset, aEntry.nSize ) == aEntry.nCrc );
+
+ if (aEntry.nCompressedSize < 0)
+ {
+ SAL_WARN("package", "bogus compressed size of: " << aEntry.nCompressedSize);
+ return false;
+ }
+
+ getSizeAndCRC( aEntry.nOffset, aEntry.nCompressedSize, &nSize, &nCRC );
+ return ( aEntry.nSize == nSize && aEntry.nCrc == nCRC );
+}
+
+sal_Int32 ZipFile::getCRC( sal_Int64 nOffset, sal_Int64 nSize )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ Sequence < sal_Int8 > aBuffer;
+ CRC32 aCRC;
+ sal_Int64 nBlockSize = ::std::min(nSize, static_cast< sal_Int64 >(32000));
+
+ aGrabber.seek( nOffset );
+ for (sal_Int64 ind = 0;
+ aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nSize;
+ ++ind)
+ {
+ sal_Int64 nLen = ::std::min(nBlockSize, nSize - ind * nBlockSize);
+ aCRC.updateSegment(aBuffer, static_cast<sal_Int32>(nLen));
+ }
+
+ return aCRC.getValue();
+}
+
+void ZipFile::getSizeAndCRC( sal_Int64 nOffset, sal_Int64 nCompressedSize, sal_Int64 *nSize, sal_Int32 *nCRC )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ Sequence < sal_Int8 > aBuffer;
+ CRC32 aCRC;
+ sal_Int64 nRealSize = 0;
+ Inflater aInflaterLocal( true );
+ sal_Int32 nBlockSize = static_cast< sal_Int32 > (::std::min( nCompressedSize, static_cast< sal_Int64 >( 32000 ) ) );
+
+ aGrabber.seek( nOffset );
+ for ( sal_Int64 ind = 0;
+ !aInflaterLocal.finished() && aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nCompressedSize;
+ ind++ )
+ {
+ Sequence < sal_Int8 > aData( nBlockSize );
+ sal_Int32 nLastInflated = 0;
+ sal_Int64 nInBlock = 0;
+
+ aInflaterLocal.setInput( aBuffer );
+ do
+ {
+ nLastInflated = aInflaterLocal.doInflateSegment( aData, 0, nBlockSize );
+ aCRC.updateSegment( aData, nLastInflated );
+ nInBlock += nLastInflated;
+ } while( !aInflater.finished() && nLastInflated );
+
+ nRealSize += nInBlock;
+ }
+
+ *nSize = nRealSize;
+ *nCRC = aCRC.getValue();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ZipOutputEntry.cxx b/package/source/zipapi/ZipOutputEntry.cxx
new file mode 100644
index 0000000000..9d9c1e9b6f
--- /dev/null
+++ b/package/source/zipapi/ZipOutputEntry.cxx
@@ -0,0 +1,404 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipOutputEntry.hxx>
+
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+
+#include <osl/diagnose.h>
+
+#include <PackageConstants.hxx>
+#include <ThreadedDeflater.hxx>
+#include <ZipEntry.hxx>
+#include <ZipFile.hxx>
+#include <ZipPackageStream.hxx>
+
+#include <algorithm>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+/** This class is used to deflate Zip entries
+ */
+ZipOutputEntryBase::ZipOutputEntryBase(
+ css::uno::Reference< css::io::XOutputStream > xOutput,
+ uno::Reference< uno::XComponentContext > xContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt,
+ bool checkStream)
+: m_xContext(std::move(xContext))
+, m_xOutStream(std::move(xOutput))
+, m_pCurrentEntry(&rEntry)
+, m_nDigested(0)
+, m_pCurrentStream(pStream)
+, m_bEncryptCurrentEntry(bEncrypt)
+{
+ assert(m_pCurrentEntry->nMethod == DEFLATED && "Use ZipPackageStream::rawWrite() for STORED entries");
+ (void)checkStream;
+ assert(!checkStream || m_xOutStream.is());
+ if (m_bEncryptCurrentEntry)
+ {
+ m_xCipherContext = ZipFile::StaticGetCipher( m_xContext, pStream->GetEncryptionData(), true );
+ if (pStream->GetEncryptionData()->m_oCheckAlg)
+ {
+ assert(pStream->GetEncryptionData()->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C);
+ m_xDigestContext = ZipFile::StaticGetDigestContextForChecksum(m_xContext, pStream->GetEncryptionData());
+ }
+ }
+}
+
+void ZipOutputEntryBase::closeEntry()
+{
+ finishDeflater();
+
+ if ((m_pCurrentEntry->nFlag & 8) == 0)
+ {
+ if (m_pCurrentEntry->nSize != getDeflaterTotalIn())
+ {
+ OSL_FAIL("Invalid entry size");
+ }
+ if (m_pCurrentEntry->nCompressedSize != getDeflaterTotalOut())
+ {
+ // Different compression strategies make the merit of this
+ // test somewhat dubious
+ m_pCurrentEntry->nCompressedSize = getDeflaterTotalOut();
+ }
+ if (m_pCurrentEntry->nCrc != m_aCRC.getValue())
+ {
+ OSL_FAIL("Invalid entry CRC-32");
+ }
+ }
+ else
+ {
+ if ( !m_bEncryptCurrentEntry )
+ {
+ m_pCurrentEntry->nSize = getDeflaterTotalIn();
+ m_pCurrentEntry->nCompressedSize = getDeflaterTotalOut();
+ }
+ m_pCurrentEntry->nCrc = m_aCRC.getValue();
+ }
+ deflaterReset();
+ m_aCRC.reset();
+
+ if (!m_bEncryptCurrentEntry)
+ return;
+
+ m_xCipherContext.clear();
+
+ uno::Sequence< sal_Int8 > aDigestSeq;
+ if ( m_xDigestContext.is() )
+ {
+ aDigestSeq = m_xDigestContext->finalizeDigestAndDispose();
+ m_xDigestContext.clear();
+ }
+
+ if ( m_pCurrentStream )
+ m_pCurrentStream->setDigest( aDigestSeq );
+}
+
+void ZipOutputEntryBase::processDeflated( const uno::Sequence< sal_Int8 >& deflateBuffer, sal_Int32 nLength )
+{
+ if ( nLength > 0 )
+ {
+ uno::Sequence< sal_Int8 > aTmpBuffer( deflateBuffer.getConstArray(), nLength );
+ if (m_bEncryptCurrentEntry && m_xCipherContext.is())
+ {
+ // Need to update our digest before encryption...
+ sal_Int32 nDiff = n_ConstDigestLength - m_nDigested;
+ if (m_xDigestContext.is() && nDiff)
+ {
+ sal_Int32 nEat = ::std::min( nLength, nDiff );
+ uno::Sequence< sal_Int8 > aTmpSeq( aTmpBuffer.getConstArray(), nEat );
+ m_xDigestContext->updateDigest( aTmpSeq );
+ m_nDigested = m_nDigested + static_cast< sal_Int16 >( nEat );
+ }
+
+ // FIXME64: uno::Sequence not 64bit safe.
+ uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->convertWithCipherContext( aTmpBuffer );
+
+ m_xOutStream->writeBytes( aEncryptionBuffer );
+
+ // the sizes as well as checksum for encrypted streams is calculated here
+ m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
+ m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
+ m_aCRC.update( aEncryptionBuffer );
+ }
+ else
+ {
+ m_xOutStream->writeBytes ( aTmpBuffer );
+ }
+ }
+
+ if (!(isDeflaterFinished() && m_bEncryptCurrentEntry && m_xCipherContext.is()))
+ return;
+
+ // FIXME64: sequence not 64bit safe.
+ uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->finalizeCipherContextAndDispose();
+ if ( aEncryptionBuffer.hasElements() )
+ {
+ m_xOutStream->writeBytes( aEncryptionBuffer );
+
+ // the sizes as well as checksum for encrypted streams are calculated here
+ m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
+ m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
+ m_aCRC.update( aEncryptionBuffer );
+ }
+}
+
+void ZipOutputEntryBase::processInput( const uno::Sequence< sal_Int8 >& rBuffer )
+{
+ if (!m_bEncryptCurrentEntry)
+ m_aCRC.updateSegment(rBuffer, rBuffer.getLength());
+}
+
+ZipOutputEntry::ZipOutputEntry(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutput,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt,
+ bool checkStream)
+: ZipOutputEntryBase(rxOutput, rxContext, rEntry, pStream, bEncrypt, checkStream)
+, m_aDeflateBuffer(n_ConstBufferSize)
+, m_aDeflater(DEFAULT_COMPRESSION, true)
+{
+}
+
+ZipOutputEntry::ZipOutputEntry(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutput,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt)
+: ZipOutputEntry( rxOutput, rxContext, rEntry, pStream, bEncrypt, true)
+{
+}
+
+void ZipOutputEntry::write( const Sequence< sal_Int8 >& rBuffer )
+{
+ if (!m_aDeflater.finished())
+ {
+ m_aDeflater.setInputSegment(rBuffer);
+ while (!m_aDeflater.needsInput())
+ doDeflate();
+ processInput(rBuffer);
+ }
+}
+
+void ZipOutputEntry::doDeflate()
+{
+ sal_Int32 nLength = m_aDeflater.doDeflateSegment(m_aDeflateBuffer, m_aDeflateBuffer.getLength());
+ processDeflated( m_aDeflateBuffer, nLength );
+}
+
+void ZipOutputEntry::finishDeflater()
+{
+ m_aDeflater.finish();
+ while (!m_aDeflater.finished())
+ doDeflate();
+}
+
+sal_Int64 ZipOutputEntry::getDeflaterTotalIn() const
+{
+ return m_aDeflater.getTotalIn();
+}
+
+sal_Int64 ZipOutputEntry::getDeflaterTotalOut() const
+{
+ return m_aDeflater.getTotalOut();
+}
+
+void ZipOutputEntry::deflaterReset()
+{
+ m_aDeflater.reset();
+}
+
+bool ZipOutputEntry::isDeflaterFinished() const
+{
+ return m_aDeflater.finished();
+}
+
+
+ZipOutputEntryInThread::ZipOutputEntryInThread(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt)
+: ZipOutputEntry( uno::Reference< css::io::XOutputStream >(), rxContext, rEntry, pStream, bEncrypt, false )
+, m_bFinished(false)
+{
+}
+
+void ZipOutputEntryInThread::createBufferFile()
+{
+ assert(!m_xOutStream && !m_xTempFile &&
+ "should only be called in the threaded mode where there is no existing stream yet");
+ m_xTempFile = new utl::TempFileFastService;
+ m_xOutStream = m_xTempFile->getOutputStream();
+}
+
+void ZipOutputEntryInThread::closeBufferFile()
+{
+ m_xOutStream->closeOutput();
+ m_xOutStream.clear();
+}
+
+void ZipOutputEntryInThread::deleteBufferFile()
+{
+ assert(!m_xOutStream.is() && m_xTempFile);
+ m_xTempFile.clear();
+}
+
+uno::Reference< io::XInputStream > ZipOutputEntryInThread::getData() const
+{
+ return m_xTempFile->getInputStream();
+}
+
+class ZipOutputEntryInThread::Task : public comphelper::ThreadTask
+{
+ ZipOutputEntryInThread *mpEntry;
+ uno::Reference< io::XInputStream > mxInStream;
+
+public:
+ Task( const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, ZipOutputEntryInThread *pEntry,
+ uno::Reference< io::XInputStream > xInStream )
+ : comphelper::ThreadTask(pTag)
+ , mpEntry(pEntry)
+ , mxInStream(std::move(xInStream))
+ {}
+
+private:
+ virtual void doWork() override
+ {
+ try
+ {
+ mpEntry->createBufferFile();
+ mpEntry->writeStream(mxInStream);
+ mxInStream.clear();
+ mpEntry->closeBufferFile();
+ mpEntry->setFinished();
+ }
+ catch (...)
+ {
+ mpEntry->setParallelDeflateException(std::current_exception());
+ try
+ {
+ if (mpEntry->m_xOutStream.is())
+ mpEntry->closeBufferFile();
+ if (mpEntry->m_xTempFile)
+ mpEntry->deleteBufferFile();
+ }
+ catch (uno::Exception const&)
+ {
+ }
+ mpEntry->setFinished();
+ }
+ }
+};
+
+std::unique_ptr<comphelper::ThreadTask> ZipOutputEntryInThread::createTask(
+ const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
+ const uno::Reference< io::XInputStream >& xInStream )
+{
+ return std::make_unique<Task>(pTag, this, xInStream);
+}
+
+void ZipOutputEntry::writeStream(const uno::Reference< io::XInputStream >& xInStream)
+{
+ sal_Int32 nLength = 0;
+ uno::Sequence< sal_Int8 > aSeq(n_ConstBufferSize);
+ do
+ {
+ nLength = xInStream->readBytes(aSeq, n_ConstBufferSize);
+ if (nLength != n_ConstBufferSize)
+ aSeq.realloc(nLength);
+
+ write(aSeq);
+ }
+ while (nLength == n_ConstBufferSize);
+ closeEntry();
+}
+
+
+ZipOutputEntryParallel::ZipOutputEntryParallel(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutput,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt)
+: ZipOutputEntryBase(rxOutput, rxContext, rEntry, pStream, bEncrypt, true)
+, totalIn(0)
+, totalOut(0)
+, finished(false)
+{
+}
+
+void ZipOutputEntryParallel::writeStream(const uno::Reference< io::XInputStream >& xInStream)
+{
+ ZipUtils::ThreadedDeflater deflater( DEFAULT_COMPRESSION );
+ deflater.deflateWrite(xInStream,
+ [this](const uno::Sequence< sal_Int8 >& rBuffer, sal_Int32 nLen) {
+ if (!m_bEncryptCurrentEntry)
+ m_aCRC.updateSegment(rBuffer, nLen);
+ },
+ [this](const uno::Sequence< sal_Int8 >& rBuffer, sal_Int32 nLen) {
+ processDeflated(rBuffer, nLen);
+ }
+ );
+ finished = true;
+ processDeflated( uno::Sequence< sal_Int8 >(), 0 ); // finish encrypting, etc.
+ totalIn = deflater.getTotalIn();
+ totalOut = deflater.getTotalOut();
+ closeEntry();
+}
+
+void ZipOutputEntryParallel::finishDeflater()
+{
+ // ThreadedDeflater is called synchronously in one call, so nothing to do here.
+}
+
+sal_Int64 ZipOutputEntryParallel::getDeflaterTotalIn() const
+{
+ return totalIn;
+}
+
+sal_Int64 ZipOutputEntryParallel::getDeflaterTotalOut() const
+{
+ return totalOut;
+}
+
+void ZipOutputEntryParallel::deflaterReset()
+{
+ totalIn = 0;
+ totalOut = 0;
+ finished = false;
+}
+
+bool ZipOutputEntryParallel::isDeflaterFinished() const
+{
+ return finished;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ZipOutputStream.cxx b/package/source/zipapi/ZipOutputStream.cxx
new file mode 100644
index 0000000000..402a2930c0
--- /dev/null
+++ b/package/source/zipapi/ZipOutputStream.cxx
@@ -0,0 +1,375 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipOutputStream.hxx>
+
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <comphelper/storagehelper.hxx>
+
+#include <osl/time.h>
+#include <osl/thread.hxx>
+
+#include <PackageConstants.hxx>
+#include <ZipEntry.hxx>
+#include <ZipOutputEntry.hxx>
+#include <ZipPackageStream.hxx>
+
+#include <thread>
+
+using namespace com::sun::star;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+/** This class is used to write Zip files
+ */
+ZipOutputStream::ZipOutputStream( const uno::Reference < io::XOutputStream > &xOStream )
+: m_xStream(xOStream)
+, mpThreadTaskTag( comphelper::ThreadPool::createThreadTaskTag() )
+, m_aChucker(xOStream)
+, m_pCurrentEntry(nullptr)
+{
+}
+
+ZipOutputStream::~ZipOutputStream()
+{
+}
+
+void ZipOutputStream::setEntry( ZipEntry *pEntry )
+{
+ if (pEntry->nTime == -1)
+ pEntry->nTime = getCurrentDosTime();
+ if (pEntry->nMethod == -1)
+ pEntry->nMethod = DEFLATED;
+ pEntry->nVersion = 20;
+ pEntry->nFlag = 1 << 11;
+ if (pEntry->nSize == -1 || pEntry->nCompressedSize == -1 ||
+ pEntry->nCrc == -1)
+ {
+ pEntry->nSize = pEntry->nCompressedSize = 0;
+ pEntry->nFlag |= 8;
+ }
+}
+
+void ZipOutputStream::addDeflatingThreadTask( ZipOutputEntryInThread *pEntry, std::unique_ptr<comphelper::ThreadTask> pTask )
+{
+ comphelper::ThreadPool::getSharedOptimalPool().pushTask(std::move(pTask));
+ m_aEntries.push_back(pEntry);
+}
+
+void ZipOutputStream::rawWrite( const Sequence< sal_Int8 >& rBuffer )
+{
+ m_aChucker.WriteBytes( rBuffer );
+}
+
+void ZipOutputStream::rawCloseEntry( bool bEncrypt )
+{
+ assert(m_pCurrentEntry && "Forgot to call writeLOC()?");
+ if ( m_pCurrentEntry->nMethod == DEFLATED && ( m_pCurrentEntry->nFlag & 8 ) )
+ writeDataDescriptor(*m_pCurrentEntry);
+
+ if (bEncrypt)
+ m_pCurrentEntry->nMethod = STORED;
+
+ m_pCurrentEntry = nullptr;
+}
+
+void ZipOutputStream::consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread> pCandidate)
+{
+ //Any exceptions thrown in the threads were caught and stored for now
+ const std::exception_ptr& rCaughtException(pCandidate->getParallelDeflateException());
+ if (rCaughtException)
+ {
+ m_aDeflateException = rCaughtException; // store it for later throwing
+ // the exception handler in DeflateThreadTask should have cleaned temp file
+ return;
+ }
+
+ writeLOC(pCandidate->getZipEntry(), pCandidate->isEncrypt());
+
+ sal_Int32 nRead;
+ uno::Sequence< sal_Int8 > aSequence(n_ConstBufferSize);
+ uno::Reference< io::XInputStream > xInput = pCandidate->getData();
+ do
+ {
+ nRead = xInput->readBytes(aSequence, n_ConstBufferSize);
+ if (nRead < n_ConstBufferSize)
+ aSequence.realloc(nRead);
+
+ rawWrite(aSequence);
+ }
+ while (nRead == n_ConstBufferSize);
+ xInput.clear();
+
+ rawCloseEntry(pCandidate->isEncrypt());
+
+ pCandidate->getZipPackageStream()->successfullyWritten(pCandidate->getZipEntry());
+ pCandidate->deleteBufferFile();
+}
+
+void ZipOutputStream::consumeFinishedScheduledThreadTaskEntries()
+{
+ std::vector< ZipOutputEntryInThread* > aNonFinishedEntries;
+
+ for(ZipOutputEntryInThread* pEntry : m_aEntries)
+ {
+ if(pEntry->isFinished())
+ {
+ consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread>(pEntry));
+ }
+ else
+ {
+ aNonFinishedEntries.push_back(pEntry);
+ }
+ }
+
+ // always reset to non-consumed entries
+ m_aEntries = aNonFinishedEntries;
+}
+
+void ZipOutputStream::reduceScheduledThreadTasksToGivenNumberOrLess(std::size_t nThreadTasks)
+{
+ while(m_aEntries.size() > nThreadTasks)
+ {
+ consumeFinishedScheduledThreadTaskEntries();
+
+ if(m_aEntries.size() > nThreadTasks)
+ {
+ std::this_thread::sleep_for(std::chrono::microseconds(100));
+ }
+ }
+}
+
+void ZipOutputStream::finish()
+{
+ assert(!m_aZipList.empty() && "Zip file must have at least one entry!");
+
+ // Wait for all thread tasks to finish & write
+ comphelper::ThreadPool::getSharedOptimalPool().waitUntilDone(mpThreadTaskTag);
+
+ // consume all processed entries
+ while(!m_aEntries.empty())
+ {
+ ZipOutputEntryInThread* pCandidate = m_aEntries.back();
+ m_aEntries.pop_back();
+ consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread>(pCandidate));
+ }
+
+ sal_Int32 nOffset= static_cast < sal_Int32 > (m_aChucker.GetPosition());
+ for (ZipEntry* p : m_aZipList)
+ {
+ writeCEN( *p );
+ delete p;
+ }
+ writeEND( nOffset, static_cast < sal_Int32 > (m_aChucker.GetPosition()) - nOffset);
+ m_aZipList.clear();
+
+ if (m_aDeflateException)
+ { // throw once all thread tasks are finished and m_aEntries can be released
+ std::rethrow_exception(m_aDeflateException);
+ }
+}
+
+const css::uno::Reference< css::io::XOutputStream >& ZipOutputStream::getStream() const
+{
+ return m_xStream;
+}
+
+void ZipOutputStream::writeEND(sal_uInt32 nOffset, sal_uInt32 nLength)
+{
+ m_aChucker.WriteInt32( ENDSIG );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt16( m_aZipList.size() );
+ m_aChucker.WriteInt16( m_aZipList.size() );
+ m_aChucker.WriteUInt32( nLength );
+ m_aChucker.WriteUInt32( nOffset );
+ m_aChucker.WriteInt16( 0 );
+}
+
+static sal_uInt32 getTruncated( sal_Int64 nNum, bool *pIsTruncated )
+{
+ if( nNum >= 0xffffffff )
+ {
+ *pIsTruncated = true;
+ return 0xffffffff;
+ }
+ else
+ return static_cast< sal_uInt32 >( nNum );
+}
+
+void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
+{
+ if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
+ throw IOException("Unexpected character is used in file name." );
+
+ OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
+ sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
+
+ m_aChucker.WriteInt32( CENSIG );
+ m_aChucker.WriteInt16( rEntry.nVersion );
+ m_aChucker.WriteInt16( rEntry.nVersion );
+ m_aChucker.WriteInt16( rEntry.nFlag );
+ m_aChucker.WriteInt16( rEntry.nMethod );
+ bool bWrite64Header = false;
+
+ m_aChucker.WriteUInt32( rEntry.nTime );
+ m_aChucker.WriteUInt32( rEntry.nCrc );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
+ sal_uInt32 nOffset32bit = getTruncated( rEntry.nOffset, &bWrite64Header );
+ m_aChucker.WriteInt16(nNameLength);
+ m_aChucker.WriteInt16( bWrite64Header? 32 : 0 ); //in ZIP64 case extra field is 32byte
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt32( 0 );
+ m_aChucker.WriteUInt32( nOffset32bit );
+
+ Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
+ m_aChucker.WriteBytes( aSequence );
+
+ if (bWrite64Header)
+ {
+ writeExtraFields( rEntry );
+ }
+}
+
+void ZipOutputStream::writeDataDescriptor(const ZipEntry& rEntry)
+{
+ bool bWrite64Header = false;
+
+ m_aChucker.WriteInt32( EXTSIG );
+ m_aChucker.WriteUInt32( rEntry.nCrc );
+ // For ZIP64(tm) format archives, the compressed and uncompressed sizes are 8 bytes each.
+ // TODO: Not sure if this is the "when ZIP64(tm) format is used"
+ bWrite64Header = rEntry.nCompressedSize >= 0x100000000 || rEntry.nSize >= 0x100000000;
+ if (!bWrite64Header)
+ {
+ m_aChucker.WriteUInt32( static_cast<sal_uInt32>(rEntry.nCompressedSize) );
+ m_aChucker.WriteUInt32( static_cast<sal_uInt32>(rEntry.nSize) );
+ }
+ else
+ {
+ m_aChucker.WriteUInt64( rEntry.nCompressedSize );
+ m_aChucker.WriteUInt64( rEntry.nSize );
+ }
+}
+
+void ZipOutputStream::writeExtraFields(const ZipEntry& rEntry)
+{
+ //Could contain more fields, now we only save Zip64 extended information
+ m_aChucker.WriteInt16( 1 ); //id of Zip64 extended information extra field
+ m_aChucker.WriteInt16( 28 ); //data size of this field = 3*8+4 byte
+ m_aChucker.WriteUInt64( rEntry.nSize );
+ m_aChucker.WriteUInt64( rEntry.nCompressedSize );
+ m_aChucker.WriteUInt64( rEntry.nOffset );
+ m_aChucker.WriteInt32( 0 ); //Number of the disk on which this file starts
+}
+
+void ZipOutputStream::writeLOC( ZipEntry *pEntry, bool bEncrypt )
+{
+ assert(!m_pCurrentEntry && "Forgot to close an entry with rawCloseEntry()?");
+ m_pCurrentEntry = pEntry;
+ m_aZipList.push_back( m_pCurrentEntry );
+ const ZipEntry &rEntry = *m_pCurrentEntry;
+
+ if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
+ throw IOException("Unexpected character is used in file name." );
+
+ OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
+ sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
+
+ m_aChucker.WriteInt32( LOCSIG );
+ m_aChucker.WriteInt16( rEntry.nVersion );
+
+ m_aChucker.WriteInt16( rEntry.nFlag );
+ // If it's an encrypted entry, we pretend its stored plain text
+ if (bEncrypt)
+ m_aChucker.WriteInt16( STORED );
+ else
+ m_aChucker.WriteInt16( rEntry.nMethod );
+
+ bool bWrite64Header = false;
+
+ m_aChucker.WriteUInt32( rEntry.nTime );
+ if ((rEntry.nFlag & 8) == 8 )
+ {
+ m_aChucker.WriteInt32( 0 );
+ m_aChucker.WriteInt32( 0 );
+ m_aChucker.WriteInt32( 0 );
+ }
+ else
+ {
+ m_aChucker.WriteUInt32( rEntry.nCrc );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
+ }
+ m_aChucker.WriteInt16( nNameLength );
+ m_aChucker.WriteInt16( bWrite64Header ? 32 : 0 );
+
+ Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
+ m_aChucker.WriteBytes( aSequence );
+
+ m_pCurrentEntry->nOffset = m_aChucker.GetPosition() - (LOCHDR + nNameLength);
+
+ if (bWrite64Header)
+ {
+ writeExtraFields(rEntry);
+ }
+}
+
+sal_uInt32 ZipOutputStream::getCurrentDosTime()
+{
+ oslDateTime aDateTime;
+ TimeValue aTimeValue;
+ osl_getSystemTime ( &aTimeValue );
+ osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime);
+
+ // at year 2108, there is an overflow
+ // -> some decision needs to be made
+ // how to handle the ZIP file format (just overflow?)
+
+ // if the current system time is before 1980,
+ // then the time traveller will have to make a decision
+ // how to handle the ZIP file format before it is invented
+ // (just underflow?)
+
+ assert(aDateTime.Year > 1980 && aDateTime.Year < 2108);
+
+ sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year);
+
+ if (nYear>=1980)
+ nYear-=1980;
+ else if (nYear>=80)
+ {
+ nYear-=80;
+ }
+ sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) +
+ ( 32 * (aDateTime.Month)) +
+ ( 512 * nYear ) ) << 16) |
+ ( ( aDateTime.Seconds/2) +
+ ( 32 * aDateTime.Minutes) +
+ ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) );
+ return nResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/blowfishcontext.cxx b/package/source/zipapi/blowfishcontext.cxx
new file mode 100644
index 0000000000..1b5ed4a148
--- /dev/null
+++ b/package/source/zipapi/blowfishcontext.cxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <rtl/cipher.h>
+#include <rtl/ref.hxx>
+
+#include "blowfishcontext.hxx"
+
+using namespace ::com::sun::star;
+
+// static
+uno::Reference< xml::crypto::XCipherContext > BlowfishCFB8CipherContext::Create( const uno::Sequence< sal_Int8 >& aDerivedKey, const uno::Sequence< sal_Int8 >& aInitVector, bool bEncrypt )
+{
+ ::rtl::Reference< BlowfishCFB8CipherContext > xResult = new BlowfishCFB8CipherContext();
+ xResult->m_pCipher = rtl_cipher_create( rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream );
+ if ( !xResult->m_pCipher )
+ throw uno::RuntimeException("Can not create cipher!" );
+
+ if ( rtl_Cipher_E_None != rtl_cipher_init(
+ xResult->m_pCipher,
+ bEncrypt ? rtl_Cipher_DirectionEncode : rtl_Cipher_DirectionDecode,
+ reinterpret_cast< const sal_uInt8* >( aDerivedKey.getConstArray() ),
+ aDerivedKey.getLength(),
+ reinterpret_cast< const sal_uInt8* >( aInitVector.getConstArray() ),
+ aInitVector.getLength() ) )
+ {
+ throw uno::RuntimeException("Can not initialize cipher!" );
+ }
+
+ xResult->m_bEncrypt = bEncrypt;
+
+ return xResult;
+}
+
+BlowfishCFB8CipherContext::~BlowfishCFB8CipherContext()
+{
+ if ( m_pCipher )
+ {
+ rtl_cipher_destroy ( m_pCipher );
+ m_pCipher = nullptr;
+ }
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL BlowfishCFB8CipherContext::convertWithCipherContext( const uno::Sequence< ::sal_Int8 >& aData )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_pCipher )
+ throw lang::DisposedException();
+
+ uno::Sequence< sal_Int8 > aResult( aData.getLength() );
+ rtlCipherError nError = rtl_Cipher_E_None;
+
+ if ( m_bEncrypt )
+ {
+ nError = rtl_cipher_encode( m_pCipher,
+ aData.getConstArray(),
+ aData.getLength(),
+ reinterpret_cast< sal_uInt8* >( aResult.getArray() ),
+ aResult.getLength() );
+ }
+ else
+ {
+ nError = rtl_cipher_decode( m_pCipher,
+ aData.getConstArray(),
+ aData.getLength(),
+ reinterpret_cast< sal_uInt8* >( aResult.getArray() ),
+ aResult.getLength() );
+ }
+
+ if ( rtl_Cipher_E_None != nError )
+ {
+ throw uno::RuntimeException("Can not decrypt/encrypt with cipher!" );
+ }
+
+ return aResult;
+}
+
+uno::Sequence< ::sal_Int8 > SAL_CALL BlowfishCFB8CipherContext::finalizeCipherContextAndDispose()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_pCipher )
+ throw lang::DisposedException();
+
+ rtl_cipher_destroy ( m_pCipher );
+ m_pCipher = nullptr;
+
+ return uno::Sequence< sal_Int8 >();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/blowfishcontext.hxx b/package/source/zipapi/blowfishcontext.hxx
new file mode 100644
index 0000000000..c0b603c152
--- /dev/null
+++ b/package/source/zipapi/blowfishcontext.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_BLOWFISHCONTEXT_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_BLOWFISHCONTEXT_HXX
+
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+
+class BlowfishCFB8CipherContext : public cppu::WeakImplHelper< css::xml::crypto::XCipherContext >
+{
+ std::mutex m_aMutex;
+ void* m_pCipher;
+ bool m_bEncrypt;
+
+ BlowfishCFB8CipherContext()
+ : m_pCipher( nullptr )
+ , m_bEncrypt( false )
+ {}
+
+public:
+
+ virtual ~BlowfishCFB8CipherContext() override;
+
+ static css::uno::Reference< css::xml::crypto::XCipherContext >
+ Create( const css::uno::Sequence< sal_Int8 >& aDerivedKey, const css::uno::Sequence< sal_Int8 >& aInitVector, bool bEncrypt );
+
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL convertWithCipherContext( const css::uno::Sequence< ::sal_Int8 >& aData ) override;
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL finalizeCipherContextAndDispose( ) override;
+};
+
+#endif // INCLUDED_PACKAGE_SOURCE_ZIPAPI_BLOWFISHCONTEXT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/sha1context.cxx b/package/source/zipapi/sha1context.cxx
new file mode 100644
index 0000000000..4b6cbd2d33
--- /dev/null
+++ b/package/source/zipapi/sha1context.cxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <rtl/digest.h>
+#include <rtl/ref.hxx>
+
+#include "sha1context.hxx"
+
+using namespace ::com::sun::star;
+
+// static
+uno::Reference<xml::crypto::XDigestContext> StarOfficeSHA1DigestContext::Create()
+{
+ ::rtl::Reference<StarOfficeSHA1DigestContext> xResult = new StarOfficeSHA1DigestContext();
+ xResult->m_pDigest = rtl_digest_createSHA1();
+ if ( !xResult->m_pDigest )
+ throw uno::RuntimeException("Can not create cipher!" );
+
+ return xResult;
+}
+
+StarOfficeSHA1DigestContext::~StarOfficeSHA1DigestContext()
+{
+ if ( m_pDigest )
+ {
+ rtl_digest_destroySHA1( m_pDigest );
+ m_pDigest = nullptr;
+ }
+}
+
+void SAL_CALL StarOfficeSHA1DigestContext::updateDigest(const uno::Sequence<::sal_Int8>& aData)
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_pDigest )
+ throw lang::DisposedException();
+
+ if ( rtl_Digest_E_None != rtl_digest_updateSHA1( m_pDigest, aData.getConstArray(), aData.getLength() ) )
+ {
+ rtl_digest_destroySHA1( m_pDigest );
+ m_pDigest = nullptr;
+
+ throw uno::RuntimeException();
+ }
+}
+
+uno::Sequence<::sal_Int8> SAL_CALL StarOfficeSHA1DigestContext::finalizeDigestAndDispose()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_pDigest )
+ throw lang::DisposedException();
+
+ uno::Sequence< sal_Int8 > aResult( RTL_DIGEST_LENGTH_SHA1 );
+ if ( rtl_Digest_E_None != rtl_digest_getSHA1( m_pDigest, reinterpret_cast< sal_uInt8* >( aResult.getArray() ), aResult.getLength() ) )
+ {
+ rtl_digest_destroySHA1( m_pDigest );
+ m_pDigest = nullptr;
+
+ throw uno::RuntimeException();
+ }
+
+ rtl_digest_destroySHA1( m_pDigest );
+ m_pDigest = nullptr;
+
+ return aResult;
+}
+
+uno::Reference<xml::crypto::XDigestContext> CorrectSHA1DigestContext::Create()
+{
+ return new CorrectSHA1DigestContext();
+}
+
+CorrectSHA1DigestContext::CorrectSHA1DigestContext()
+{
+}
+
+CorrectSHA1DigestContext::~CorrectSHA1DigestContext()
+{
+}
+
+void SAL_CALL CorrectSHA1DigestContext::updateDigest(const uno::Sequence<::sal_Int8>& rData)
+{
+ std::scoped_lock aGuard(m_Mutex);
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ m_Hash.update(reinterpret_cast<unsigned char const*>(rData.getConstArray()), rData.getLength());
+}
+
+uno::Sequence<::sal_Int8> SAL_CALL CorrectSHA1DigestContext::finalizeDigestAndDispose()
+{
+ std::scoped_lock aGuard(m_Mutex);
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ m_bDisposed = true;
+ std::vector<unsigned char> const sha1(m_Hash.finalize());
+ return uno::Sequence<sal_Int8>(reinterpret_cast<sal_Int8 const*>(sha1.data()), sha1.size());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/sha1context.hxx b/package/source/zipapi/sha1context.hxx
new file mode 100644
index 0000000000..6cc09da01b
--- /dev/null
+++ b/package/source/zipapi/sha1context.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_SHA1CONTEXT_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_SHA1CONTEXT_HXX
+
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+
+#include <comphelper/hash.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+
+class StarOfficeSHA1DigestContext
+ : public cppu::WeakImplHelper<css::xml::crypto::XDigestContext>
+{
+ std::mutex m_aMutex;
+ void* m_pDigest;
+
+ StarOfficeSHA1DigestContext()
+ : m_pDigest( nullptr )
+ {}
+
+public:
+
+ virtual ~StarOfficeSHA1DigestContext() override;
+
+ static css::uno::Reference< css::xml::crypto::XDigestContext > Create();
+
+ virtual void SAL_CALL updateDigest( const css::uno::Sequence< ::sal_Int8 >& aData ) override;
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL finalizeDigestAndDispose() override;
+
+};
+
+class CorrectSHA1DigestContext
+ : public cppu::WeakImplHelper<css::xml::crypto::XDigestContext>
+{
+ std::mutex m_Mutex;
+ ::comphelper::Hash m_Hash{::comphelper::HashType::SHA1};
+ bool m_bDisposed{false};
+
+ CorrectSHA1DigestContext();
+
+public:
+
+ virtual ~CorrectSHA1DigestContext() override;
+
+ static css::uno::Reference<css::xml::crypto::XDigestContext> Create();
+
+ virtual void SAL_CALL updateDigest(const css::uno::Sequence<::sal_Int8>& rData) override;
+ virtual css::uno::Sequence<::sal_Int8> SAL_CALL finalizeDigestAndDispose() override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx
new file mode 100644
index 0000000000..02f7cf71e8
--- /dev/null
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -0,0 +1,1961 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipPackage.hxx>
+#include "ZipPackageSink.hxx"
+#include <ZipEnumeration.hxx>
+#include <ZipPackageStream.hxx>
+#include <ZipPackageFolder.hxx>
+#include <ZipOutputEntry.hxx>
+#include <ZipOutputStream.hxx>
+#include <ZipPackageBuffer.hxx>
+#include <ZipFile.hxx>
+#include <PackageConstants.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/packages/zip/ZipException.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/packages/manifest/ManifestReader.hpp>
+#include <com/sun/star/packages/manifest/ManifestWriter.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <comphelper/fileurl.hxx>
+#include <comphelper/processfactory.hxx>
+#include <ucbhelper/content.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/io/XActiveDataStreamer.hpp>
+#include <com/sun/star/embed/UseBackupException.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/KDFID.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/random.h>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
+#include <com/sun/star/io/XAsyncOutputMonitor.hpp>
+
+#include <string_view>
+
+#include <comphelper/seekableinput.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <utility>
+
+using namespace osl;
+using namespace cppu;
+using namespace ucbhelper;
+using namespace com::sun::star;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::util;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::task;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::packages;
+using namespace com::sun::star::container;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::packages::manifest;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+namespace {
+
+class ActiveDataStreamer : public ::cppu::WeakImplHelper< XActiveDataStreamer >
+{
+ uno::Reference< XStream > mStream;
+public:
+
+ virtual uno::Reference< XStream > SAL_CALL getStream() override
+ { return mStream; }
+
+ virtual void SAL_CALL setStream( const uno::Reference< XStream >& stream ) override
+ { mStream = stream; }
+};
+
+class DummyInputStream : public ::cppu::WeakImplHelper< XInputStream >
+{
+ virtual sal_Int32 SAL_CALL readBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
+ { return 0; }
+
+ virtual sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
+ { return 0; }
+
+ virtual void SAL_CALL skipBytes( sal_Int32 ) override
+ {}
+
+ virtual sal_Int32 SAL_CALL available() override
+ { return 0; }
+
+ virtual void SAL_CALL closeInput() override
+ {}
+};
+
+}
+
+ZipPackage::ZipPackage ( uno::Reference < XComponentContext > xContext )
+: m_aMutexHolder( new comphelper::RefCountedMutex )
+, m_nStartKeyGenerationID( xml::crypto::DigestID::SHA1 )
+, m_oChecksumDigestID( xml::crypto::DigestID::SHA1_1K )
+, m_nKeyDerivationFunctionID(xml::crypto::KDFID::PBKDF2)
+, m_nCommonEncryptionID( xml::crypto::CipherID::BLOWFISH_CFB_8 )
+, m_bHasEncryptedEntries ( false )
+, m_bHasNonEncryptedEntries ( false )
+, m_bInconsistent ( false )
+, m_bForceRecovery ( false )
+, m_bMediaTypeFallbackUsed ( false )
+, m_nFormat( embed::StorageFormats::PACKAGE ) // package is the default format
+, m_bAllowRemoveOnInsert( true )
+, m_eMode ( e_IMode_None )
+, m_xContext(std::move( xContext ))
+{
+ m_xRootFolder = new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
+}
+
+ZipPackage::~ZipPackage()
+{
+}
+
+bool ZipPackage::isLocalFile() const
+{
+ return comphelper::isFileUrl(m_aURL);
+}
+
+void ZipPackage::parseManifest()
+{
+ if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ return;
+
+ bool bManifestParsed = false;
+ static constexpr OUString sMeta (u"META-INF"_ustr);
+ if ( m_xRootFolder->hasByName( sMeta ) )
+ {
+ try {
+ static constexpr OUString sManifest (u"manifest.xml"_ustr);
+ Any aAny = m_xRootFolder->getByName( sMeta );
+ uno::Reference< XNameContainer > xMetaInfFolder;
+ aAny >>= xMetaInfFolder;
+ if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
+ {
+ uno::Reference < XActiveDataSink > xSink;
+ aAny = xMetaInfFolder->getByName( sManifest );
+ aAny >>= xSink;
+ if ( xSink.is() )
+ {
+ uno::Reference < XManifestReader > xReader = ManifestReader::create( m_xContext );
+
+ static constexpr OUStringLiteral sPropFullPath (u"FullPath");
+ static constexpr OUStringLiteral sPropVersion (u"Version");
+ static constexpr OUStringLiteral sPropMediaType (u"MediaType");
+ static constexpr OUStringLiteral sPropInitialisationVector (u"InitialisationVector");
+ static constexpr OUStringLiteral sPropSalt (u"Salt");
+ static constexpr OUStringLiteral sPropIterationCount (u"IterationCount");
+ static constexpr OUStringLiteral sPropSize (u"Size");
+ static constexpr OUStringLiteral sPropDigest (u"Digest");
+ static constexpr OUStringLiteral sPropDerivedKeySize (u"DerivedKeySize");
+ static constexpr OUStringLiteral sPropDigestAlgorithm (u"DigestAlgorithm");
+ static constexpr OUStringLiteral sPropEncryptionAlgorithm (u"EncryptionAlgorithm");
+ static constexpr OUStringLiteral sPropStartKeyAlgorithm (u"StartKeyAlgorithm");
+ static constexpr OUStringLiteral sKeyInfo (u"KeyInfo");
+
+ const uno::Sequence < uno::Sequence < PropertyValue > > aManifestSequence = xReader->readManifestSequence ( xSink->getInputStream() );
+ const Any *pKeyInfo = nullptr;
+
+ for ( const uno::Sequence<PropertyValue>& rSequence : aManifestSequence )
+ {
+ OUString sPath, sMediaType, sVersion;
+ const Any *pSalt = nullptr, *pVector = nullptr, *pCount = nullptr, *pSize = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptionAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
+ uno::Any const* pKDF = nullptr;
+ uno::Any const* pArgon2Args = nullptr;
+ for ( const PropertyValue& rValue : rSequence )
+ {
+ if ( rValue.Name == sPropFullPath )
+ rValue.Value >>= sPath;
+ else if ( rValue.Name == sPropVersion )
+ rValue.Value >>= sVersion;
+ else if ( rValue.Name == sPropMediaType )
+ rValue.Value >>= sMediaType;
+ else if ( rValue.Name == sPropSalt )
+ pSalt = &( rValue.Value );
+ else if ( rValue.Name == sPropInitialisationVector )
+ pVector = &( rValue.Value );
+ else if ( rValue.Name == sPropIterationCount )
+ pCount = &( rValue.Value );
+ else if ( rValue.Name == sPropSize )
+ pSize = &( rValue.Value );
+ else if ( rValue.Name == sPropDigest )
+ pDigest = &( rValue.Value );
+ else if ( rValue.Name == sPropDigestAlgorithm )
+ pDigestAlg = &( rValue.Value );
+ else if ( rValue.Name == sPropEncryptionAlgorithm )
+ pEncryptionAlg = &( rValue.Value );
+ else if ( rValue.Name == sPropStartKeyAlgorithm )
+ pStartKeyAlg = &( rValue.Value );
+ else if ( rValue.Name == sPropDerivedKeySize )
+ pDerivedKeySize = &( rValue.Value );
+ else if ( rValue.Name == sKeyInfo )
+ pKeyInfo = &( rValue.Value );
+ else if (rValue.Name == "KeyDerivationFunction") {
+ pKDF = &rValue.Value;
+ } else if (rValue.Name == "Argon2Args") {
+ pArgon2Args = &rValue.Value;
+ }
+ }
+
+ if ( !sPath.isEmpty() && hasByHierarchicalName ( sPath ) )
+ {
+ aAny = getByHierarchicalName( sPath );
+ uno::Reference < XInterface > xTmp;
+ aAny >>= xTmp;
+ if (auto pFolder = dynamic_cast<ZipPackageFolder*>(xTmp.get()))
+ {
+ pFolder->SetMediaType ( sMediaType );
+ pFolder->SetVersion ( sVersion );
+ }
+ else if (auto pStream = dynamic_cast<ZipPackageStream*>(xTmp.get()))
+ {
+ pStream->SetMediaType ( sMediaType );
+ pStream->SetFromManifest( true );
+
+ if (pKeyInfo
+ && pVector && pSize && pEncryptionAlg
+ && pKDF && pKDF->has<sal_Int32>() && pKDF->get<sal_Int32>() == xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P
+ && ((pEncryptionAlg->has<sal_Int32>()
+ && pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
+ || (pDigest && pDigestAlg)))
+ {
+ uno::Sequence < sal_Int8 > aSequence;
+ sal_Int64 nSize = 0;
+ ::std::optional<sal_Int32> oDigestAlg;
+ sal_Int32 nEncryptionAlg = 0;
+
+ pStream->SetToBeEncrypted ( true );
+
+ *pVector >>= aSequence;
+ pStream->setInitialisationVector ( aSequence );
+
+ *pSize >>= nSize;
+ pStream->setSize ( nSize );
+
+ if (pDigest && pDigestAlg)
+ {
+ *pDigest >>= aSequence;
+ pStream->setDigest(aSequence);
+
+ assert(pDigestAlg->has<sal_Int32>());
+ oDigestAlg.emplace(pDigestAlg->get<sal_Int32>());
+ pStream->SetImportedChecksumAlgorithm(oDigestAlg);
+ }
+
+ *pEncryptionAlg >>= nEncryptionAlg;
+ pStream->SetImportedEncryptionAlgorithm( nEncryptionAlg );
+
+ *pKeyInfo >>= m_aGpgProps;
+
+ pStream->SetToBeCompressed ( true );
+ pStream->SetToBeEncrypted ( true );
+ pStream->SetIsEncrypted ( true );
+ pStream->setIterationCount(::std::optional<sal_Int32>());
+ pStream->setArgon2Args(::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>>());
+
+ // clamp to default SHA256 start key magic value,
+ // c.f. ZipPackageStream::GetEncryptionKey()
+ // trying to get key value from properties
+ const sal_Int32 nStartKeyAlg = xml::crypto::DigestID::SHA256;
+ pStream->SetImportedStartKeyAlgorithm( nStartKeyAlg );
+
+ if (!m_bHasEncryptedEntries
+ && (pStream->getName() == "content.xml"
+ || pStream->getName() == "encrypted-package"))
+ {
+ m_bHasEncryptedEntries = true;
+ m_oChecksumDigestID = oDigestAlg;
+ m_nKeyDerivationFunctionID = xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
+ m_nCommonEncryptionID = nEncryptionAlg;
+ m_nStartKeyGenerationID = nStartKeyAlg;
+ }
+ }
+ else if (pSalt
+ && pVector && pSize && pEncryptionAlg
+ && pKDF && pKDF->has<sal_Int32>()
+ && ((pKDF->get<sal_Int32>() == xml::crypto::KDFID::PBKDF2 && pCount)
+ || (pKDF->get<sal_Int32>() == xml::crypto::KDFID::Argon2id && pArgon2Args))
+ && ((pEncryptionAlg->has<sal_Int32>()
+ && pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
+ || (pDigest && pDigestAlg)))
+
+ {
+ uno::Sequence < sal_Int8 > aSequence;
+ sal_Int64 nSize = 0;
+ ::std::optional<sal_Int32> oDigestAlg;
+ sal_Int32 nKDF = 0;
+ sal_Int32 nEncryptionAlg = 0;
+ sal_Int32 nCount = 0;
+ sal_Int32 nDerivedKeySize = 16, nStartKeyAlg = xml::crypto::DigestID::SHA1;
+
+ pStream->SetToBeEncrypted ( true );
+
+ *pSalt >>= aSequence;
+ pStream->setSalt ( aSequence );
+
+ *pVector >>= aSequence;
+ pStream->setInitialisationVector ( aSequence );
+
+ *pKDF >>= nKDF;
+
+ if (pCount)
+ {
+ *pCount >>= nCount;
+ pStream->setIterationCount(::std::optional<sal_Int32>(nCount));
+ }
+
+ if (pArgon2Args)
+ {
+ uno::Sequence<sal_Int32> args;
+ *pArgon2Args >>= args;
+ assert(args.getLength() == 3);
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgs;
+ oArgs.emplace(args[0], args[1], args[2]);
+ pStream->setArgon2Args(oArgs);
+ }
+
+ *pSize >>= nSize;
+ pStream->setSize ( nSize );
+
+ if (pDigest && pDigestAlg)
+ {
+ *pDigest >>= aSequence;
+ pStream->setDigest(aSequence);
+
+ assert(pDigestAlg->has<sal_Int32>());
+ oDigestAlg.emplace(pDigestAlg->get<sal_Int32>());
+ pStream->SetImportedChecksumAlgorithm(oDigestAlg);
+ }
+
+ *pEncryptionAlg >>= nEncryptionAlg;
+ pStream->SetImportedEncryptionAlgorithm( nEncryptionAlg );
+
+ if ( pDerivedKeySize )
+ *pDerivedKeySize >>= nDerivedKeySize;
+ pStream->SetImportedDerivedKeySize( nDerivedKeySize );
+
+ if ( pStartKeyAlg )
+ *pStartKeyAlg >>= nStartKeyAlg;
+ pStream->SetImportedStartKeyAlgorithm( nStartKeyAlg );
+
+ pStream->SetToBeCompressed ( true );
+ pStream->SetToBeEncrypted ( true );
+ pStream->SetIsEncrypted ( true );
+ if (!m_bHasEncryptedEntries
+ && (pStream->getName() == "content.xml"
+ || pStream->getName() == "encrypted-package"))
+ {
+ m_bHasEncryptedEntries = true;
+ m_nStartKeyGenerationID = nStartKeyAlg;
+ m_nKeyDerivationFunctionID = nKDF;
+ m_oChecksumDigestID = oDigestAlg;
+ m_nCommonEncryptionID = nEncryptionAlg;
+ }
+ }
+ else
+ m_bHasNonEncryptedEntries = true;
+ }
+ else
+ throw ZipIOException(THROW_WHERE "Wrong content");
+ }
+ }
+
+ bManifestParsed = true;
+ }
+
+ // now hide the manifest.xml file from user
+ xMetaInfFolder->removeByName( sManifest );
+ }
+ }
+ catch( Exception& )
+ {
+ if ( !m_bForceRecovery )
+ throw;
+ }
+ }
+
+ if ( !bManifestParsed && !m_bForceRecovery )
+ throw ZipIOException(
+ THROW_WHERE "Could not parse manifest.xml" );
+
+ static constexpr OUString sMimetype (u"mimetype"_ustr);
+ if ( m_xRootFolder->hasByName( sMimetype ) )
+ {
+ // get mediatype from the "mimetype" stream
+ OUString aPackageMediatype;
+ uno::Reference < io::XActiveDataSink > xMimeSink;
+ m_xRootFolder->getByName( sMimetype ) >>= xMimeSink;
+ if ( xMimeSink.is() )
+ {
+ uno::Reference< io::XInputStream > xMimeInStream = xMimeSink->getInputStream();
+ if ( xMimeInStream.is() )
+ {
+ // Mediatypes longer than 1024 symbols should not appear here
+ uno::Sequence< sal_Int8 > aData( 1024 );
+ sal_Int32 nRead = xMimeInStream->readBytes( aData, 1024 );
+ if ( nRead > aData.getLength() )
+ nRead = aData.getLength();
+
+ if ( nRead )
+ aPackageMediatype = OUString( reinterpret_cast<char const *>(aData.getConstArray()), nRead, RTL_TEXTENCODING_ASCII_US );
+ }
+ }
+
+ if (!bManifestParsed || m_xRootFolder->GetMediaType().isEmpty())
+ {
+ // the manifest.xml could not be successfully parsed, this is an inconsistent package
+ if ( aPackageMediatype.startsWith("application/vnd.") )
+ {
+ // accept only types that look similar to own mediatypes
+ m_xRootFolder->SetMediaType( aPackageMediatype );
+ // if there is an encrypted inner package, there is no root
+ // document, because instead there is a package, and it is not
+ // an error
+ if (!m_xRootFolder->hasByName("encrypted-package"))
+ {
+ m_bMediaTypeFallbackUsed = true;
+ }
+ }
+ }
+ else if ( !m_bForceRecovery )
+ {
+ // the mimetype stream should contain the same information as manifest.xml
+ OUString const mediaTypeXML(m_xRootFolder->hasByName("encrypted-package")
+ ? m_xRootFolder->doGetByName("encrypted-package").xPackageEntry->GetMediaType()
+ : m_xRootFolder->GetMediaType());
+ if (mediaTypeXML != aPackageMediatype)
+ {
+ throw ZipIOException(
+ THROW_WHERE
+ "mimetype conflicts with manifest.xml, \""
+ + mediaTypeXML + "\" vs. \""
+ + aPackageMediatype + "\"" );
+ }
+ }
+
+ m_xRootFolder->removeByName( sMimetype );
+ }
+
+ m_bInconsistent = m_xRootFolder->LookForUnexpectedODF12Streams(
+ std::u16string_view(), m_xRootFolder->hasByName("encrypted-package"));
+
+ bool bODF12AndNewer = ( m_xRootFolder->GetVersion().compareTo( ODFVER_012_TEXT ) >= 0 );
+ if ( !m_bForceRecovery && bODF12AndNewer )
+ {
+ if ( m_bInconsistent )
+ {
+ // this is an ODF1.2 document that contains streams not referred in the manifest.xml;
+ // in case of ODF1.2 documents without version in manifest.xml the property IsInconsistent
+ // should be checked later
+ throw ZipIOException(
+ THROW_WHERE "there are streams not referred in manifest.xml" );
+ }
+ // all the streams should be encrypted with the same StartKey in ODF1.2
+ // TODO/LATER: in future the exception should be thrown
+ // throw ZipIOException( THROW_WHERE "More than one Start Key Generation algorithm is specified!" );
+ }
+
+ // in case it is a correct ODF1.2 document, the version must be set
+ // and the META-INF folder is reserved for package format
+ if ( bODF12AndNewer )
+ m_xRootFolder->removeByName( sMeta );
+}
+
+void ZipPackage::parseContentType()
+{
+ if ( m_nFormat != embed::StorageFormats::OFOPXML )
+ return;
+
+ try {
+ static constexpr OUString aContentTypes(u"[Content_Types].xml"_ustr);
+ // the content type must exist in OFOPXML format!
+ if ( !m_xRootFolder->hasByName( aContentTypes ) )
+ throw io::IOException(THROW_WHERE "Wrong format!" );
+
+ uno::Reference < io::XActiveDataSink > xSink;
+ uno::Any aAny = m_xRootFolder->getByName( aContentTypes );
+ aAny >>= xSink;
+ if ( xSink.is() )
+ {
+ uno::Reference< io::XInputStream > xInStream = xSink->getInputStream();
+ if ( xInStream.is() )
+ {
+ // here aContentTypeInfo[0] - Defaults, and aContentTypeInfo[1] - Overrides
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aContentTypeInfo =
+ ::comphelper::OFOPXMLHelper::ReadContentTypeSequence( xInStream, m_xContext );
+
+ if ( aContentTypeInfo.getLength() != 2 )
+ throw io::IOException(THROW_WHERE );
+
+ // set the implicit types first
+ for ( const auto& rPair : aContentTypeInfo[0] )
+ m_xRootFolder->setChildStreamsTypeByExtension( rPair );
+
+ // now set the explicit types
+ for ( const auto& rPair : aContentTypeInfo[1] )
+ {
+ OUString aPath;
+ if ( rPair.First.toChar() == '/' )
+ aPath = rPair.First.copy( 1 );
+ else
+ aPath = rPair.First;
+
+ if ( !aPath.isEmpty() && hasByHierarchicalName( aPath ) )
+ {
+ uno::Any aIterAny = getByHierarchicalName( aPath );
+ uno::Reference < XInterface > xIterTmp;
+ aIterAny >>= xIterTmp;
+ if (auto pStream = dynamic_cast<ZipPackageStream*>(xIterTmp.get()))
+ {
+ // this is a package stream, in OFOPXML format only streams can have mediatype
+ pStream->SetMediaType( rPair.Second );
+ }
+ }
+ }
+ }
+ }
+
+ m_xRootFolder->removeByName( aContentTypes );
+ }
+ catch( uno::Exception& )
+ {
+ if ( !m_bForceRecovery )
+ throw;
+ }
+}
+
+void ZipPackage::getZipFileContents()
+{
+ ZipEnumeration aEnum = m_pZipFile->entries();
+ OUString sTemp, sDirName;
+ sal_Int32 nOldIndex, nStreamIndex;
+ FolderHash::iterator aIter;
+
+ while (aEnum.hasMoreElements())
+ {
+ nOldIndex = 0;
+ ZipPackageFolder* pCurrent = m_xRootFolder.get();
+ const ZipEntry & rEntry = *aEnum.nextElement();
+ OUString rName = rEntry.sPath;
+
+ if ( m_bForceRecovery )
+ {
+ // the PKZIP Application note version 6.2 does not allows to use '\' as separator
+ // unfortunately it is used by some implementations, so we have to support it in recovery mode
+ rName = rName.replace( '\\', '/' );
+ }
+
+ nStreamIndex = rName.lastIndexOf ( '/' );
+ if ( nStreamIndex != -1 )
+ {
+ sDirName = rName.copy ( 0, nStreamIndex );
+ aIter = m_aRecent.find ( sDirName );
+ if ( aIter != m_aRecent.end() )
+ pCurrent = ( *aIter ).second;
+ }
+
+ if ( pCurrent == m_xRootFolder.get() )
+ {
+ sal_Int32 nIndex;
+ while ( ( nIndex = rName.indexOf( '/', nOldIndex ) ) != -1 )
+ {
+ sTemp = rName.copy ( nOldIndex, nIndex - nOldIndex );
+ if ( nIndex == nOldIndex )
+ break;
+ if ( !pCurrent->hasByName( sTemp ) )
+ {
+ rtl::Reference<ZipPackageFolder> pPkgFolder = new ZipPackageFolder(m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
+ pPkgFolder->setName( sTemp );
+ pPkgFolder->doSetParent( pCurrent );
+ pCurrent = pPkgFolder.get();
+ }
+ else
+ {
+ ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
+ if (!rInfo.bFolder)
+ throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
+ pCurrent = rInfo.pFolder;
+ }
+ nOldIndex = nIndex+1;
+ }
+ if ( nStreamIndex != -1 && !sDirName.isEmpty() )
+ m_aRecent [ sDirName ] = pCurrent;
+ }
+ if ( rName.getLength() -1 != nStreamIndex )
+ {
+ nStreamIndex++;
+ sTemp = rName.copy( nStreamIndex );
+
+ if (!pCurrent->hasByName(sTemp))
+ {
+ rtl::Reference<ZipPackageStream> pPkgStream = new ZipPackageStream(*this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
+ pPkgStream->SetPackageMember(true);
+ pPkgStream->setZipEntryOnLoading(rEntry);
+ pPkgStream->setName(sTemp);
+ pPkgStream->doSetParent(pCurrent);
+ }
+ }
+ }
+
+ if ( m_nFormat == embed::StorageFormats::PACKAGE )
+ parseManifest();
+ else if ( m_nFormat == embed::StorageFormats::OFOPXML )
+ parseContentType();
+}
+
+void SAL_CALL ZipPackage::initialize( const uno::Sequence< Any >& aArguments )
+{
+ beans::NamedValue aNamedValue;
+
+ if ( !aArguments.hasElements() )
+ return;
+
+ bool bHaveZipFile = true;
+
+ for( const auto& rArgument : aArguments )
+ {
+ OUString aParamUrl;
+ if ( rArgument >>= aParamUrl )
+ {
+ m_eMode = e_IMode_URL;
+ try
+ {
+ sal_Int32 nParam = aParamUrl.indexOf( '?' );
+ if ( nParam >= 0 )
+ {
+ m_aURL = aParamUrl.copy( 0, nParam );
+ std::u16string_view aParam = aParamUrl.subView( nParam + 1 );
+
+ sal_Int32 nIndex = 0;
+ do
+ {
+ std::u16string_view aCommand = o3tl::getToken(aParam, 0, '&', nIndex );
+ if ( aCommand == u"repairpackage" )
+ {
+ m_bForceRecovery = true;
+ break;
+ }
+ else if ( aCommand == u"purezip" )
+ {
+ m_nFormat = embed::StorageFormats::ZIP;
+ m_xRootFolder->setPackageFormat_Impl( m_nFormat );
+ break;
+ }
+ else if ( aCommand == u"ofopxml" )
+ {
+ m_nFormat = embed::StorageFormats::OFOPXML;
+ m_xRootFolder->setPackageFormat_Impl( m_nFormat );
+ break;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+ else
+ m_aURL = aParamUrl;
+
+ Content aContent(
+ m_aURL, uno::Reference< XCommandEnvironment >(),
+ m_xContext );
+ Any aAny = aContent.getPropertyValue("Size");
+ sal_uInt64 aSize = 0;
+ // kind of optimization: treat empty files as nonexistent files
+ // and write to such files directly. Note that "Size" property is optional.
+ bool bHasSizeProperty = aAny >>= aSize;
+ if( !bHasSizeProperty || aSize )
+ {
+ uno::Reference < XActiveDataSink > xSink = new ZipPackageSink;
+ if ( aContent.openStream ( xSink ) )
+ m_xContentStream = xSink->getInputStream();
+ }
+ else
+ bHaveZipFile = false;
+ }
+ catch ( css::uno::Exception& )
+ {
+ // Exception derived from uno::Exception thrown. This probably
+ // means the file doesn't exist...we'll create it at
+ // commitChanges time
+ bHaveZipFile = false;
+ }
+ }
+ else if ( rArgument >>= m_xStream )
+ {
+ // a writable stream can implement both XStream & XInputStream
+ m_eMode = e_IMode_XStream;
+ m_xContentStream = m_xStream->getInputStream();
+ }
+ else if ( rArgument >>= m_xContentStream )
+ {
+ m_eMode = e_IMode_XInputStream;
+ }
+ else if ( rArgument >>= aNamedValue )
+ {
+ if ( aNamedValue.Name == "RepairPackage" )
+ aNamedValue.Value >>= m_bForceRecovery;
+ else if ( aNamedValue.Name == "PackageFormat" )
+ {
+ // setting this argument to true means Package format
+ // setting it to false means plain Zip format
+
+ bool bPackFormat = true;
+ aNamedValue.Value >>= bPackFormat;
+ if ( !bPackFormat )
+ m_nFormat = embed::StorageFormats::ZIP;
+
+ m_xRootFolder->setPackageFormat_Impl( m_nFormat );
+ }
+ else if ( aNamedValue.Name == "StorageFormat" )
+ {
+ OUString aFormatName;
+ sal_Int32 nFormatID = 0;
+ if ( aNamedValue.Value >>= aFormatName )
+ {
+ if ( aFormatName == PACKAGE_STORAGE_FORMAT_STRING )
+ m_nFormat = embed::StorageFormats::PACKAGE;
+ else if ( aFormatName == ZIP_STORAGE_FORMAT_STRING )
+ m_nFormat = embed::StorageFormats::ZIP;
+ else if ( aFormatName == OFOPXML_STORAGE_FORMAT_STRING )
+ m_nFormat = embed::StorageFormats::OFOPXML;
+ else
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+ }
+ else if ( aNamedValue.Value >>= nFormatID )
+ {
+ if (nFormatID != embed::StorageFormats::PACKAGE
+ && nFormatID != embed::StorageFormats::ZIP
+ && nFormatID != embed::StorageFormats::OFOPXML)
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ m_nFormat = nFormatID;
+ }
+ else
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ m_xRootFolder->setPackageFormat_Impl( m_nFormat );
+ }
+ else if ( aNamedValue.Name == "AllowRemoveOnInsert" )
+ {
+ aNamedValue.Value >>= m_bAllowRemoveOnInsert;
+ m_xRootFolder->setRemoveOnInsertMode_Impl( m_bAllowRemoveOnInsert );
+ }
+ else if (aNamedValue.Name == "NoFileSync")
+ aNamedValue.Value >>= m_bDisableFileSync;
+
+ // for now the progress handler is not used, probably it will never be
+ // if ( aNamedValue.Name == "ProgressHandler" )
+ }
+ else
+ {
+ // The URL is not acceptable
+ throw css::uno::Exception (THROW_WHERE "Bad arguments.",
+ getXWeak() );
+ }
+ }
+
+ try
+ {
+ if ( m_xContentStream.is() )
+ {
+ // the stream must be seekable, if it is not it will be wrapped
+ m_xContentStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xContentStream, m_xContext );
+ m_xContentSeek.set( m_xContentStream, UNO_QUERY_THROW );
+ if ( !m_xContentSeek->getLength() )
+ bHaveZipFile = false;
+ }
+ else
+ bHaveZipFile = false;
+ }
+ catch ( css::uno::Exception& )
+ {
+ // Exception derived from uno::Exception thrown. This probably
+ // means the file doesn't exist...we'll create it at
+ // commitChanges time
+ bHaveZipFile = false;
+ }
+ if ( !bHaveZipFile )
+ return;
+
+ bool bBadZipFile = false;
+ OUString message;
+ try
+ {
+ m_pZipFile.emplace(m_aMutexHolder, m_xContentStream, m_xContext, true, m_bForceRecovery);
+ getZipFileContents();
+ }
+ catch ( IOException & e )
+ {
+ bBadZipFile = true;
+ message = "IOException: " + e.Message;
+ }
+ catch ( ZipException & e )
+ {
+ bBadZipFile = true;
+ message = "ZipException: " + e.Message;
+ }
+ catch ( Exception & )
+ {
+ m_pZipFile.reset();
+ throw;
+ }
+
+ if ( bBadZipFile )
+ {
+ // clean up the memory, and tell the UCB about the error
+ m_pZipFile.reset();
+
+ throw css::packages::zip::ZipIOException (
+ THROW_WHERE "Bad Zip File, " + message,
+ getXWeak() );
+ }
+}
+
+Any SAL_CALL ZipPackage::getByHierarchicalName( const OUString& aName )
+{
+ OUString sTemp, sDirName;
+ sal_Int32 nOldIndex, nStreamIndex;
+ FolderHash::iterator aIter;
+
+ sal_Int32 nIndex = aName.getLength();
+
+ if (aName == "/")
+ // root directory.
+ return Any ( uno::Reference( cppu::getXWeak(m_xRootFolder.get()) ) );
+
+ nStreamIndex = aName.lastIndexOf ( '/' );
+ bool bFolder = nStreamIndex == nIndex-1; // last character is '/'.
+
+ if ( nStreamIndex != -1 )
+ {
+ // The name contains '/'.
+ sDirName = aName.copy ( 0, nStreamIndex );
+ aIter = m_aRecent.find ( sDirName );
+ if ( aIter != m_aRecent.end() )
+ {
+ // There is a cached entry for this name.
+
+ ZipPackageFolder* pFolder = aIter->second;
+
+ if ( bFolder )
+ {
+ // Determine the directory name.
+ sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
+ sTemp = aName.copy ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
+
+ if (pFolder && sTemp == pFolder->getName())
+ return Any(uno::Reference(cppu::getXWeak(pFolder)));
+ }
+ else
+ {
+ // Determine the file name.
+ sTemp = aName.copy ( nStreamIndex + 1 );
+
+ if (pFolder && pFolder->hasByName(sTemp))
+ return pFolder->getByName(sTemp);
+ }
+
+ m_aRecent.erase( aIter );
+ }
+ }
+ else if ( m_xRootFolder->hasByName ( aName ) )
+ // top-level element.
+ return m_xRootFolder->getByName ( aName );
+
+ // Not in the cache. Search normally.
+
+ nOldIndex = 0;
+ ZipPackageFolder * pCurrent = m_xRootFolder.get();
+ ZipPackageFolder * pPrevious = nullptr;
+
+ // Find the right directory for the given path.
+
+ while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
+ {
+ sTemp = aName.copy ( nOldIndex, nIndex - nOldIndex );
+ if ( nIndex == nOldIndex )
+ break;
+ if ( !pCurrent->hasByName( sTemp ) )
+ throw NoSuchElementException(THROW_WHERE );
+
+ pPrevious = pCurrent;
+ ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
+ if (!rInfo.bFolder)
+ throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
+ pCurrent = rInfo.pFolder;
+ nOldIndex = nIndex+1;
+ }
+
+ if ( bFolder )
+ {
+ if ( nStreamIndex != -1 )
+ m_aRecent[sDirName] = pPrevious; // cache it.
+ return Any ( uno::Reference( cppu::getXWeak(pCurrent) ) );
+ }
+
+ sTemp = aName.copy( nOldIndex );
+
+ if ( pCurrent->hasByName ( sTemp ) )
+ {
+ if ( nStreamIndex != -1 )
+ m_aRecent[sDirName] = pCurrent; // cache it.
+ return pCurrent->getByName( sTemp );
+ }
+
+ throw NoSuchElementException(THROW_WHERE);
+}
+
+sal_Bool SAL_CALL ZipPackage::hasByHierarchicalName( const OUString& aName )
+{
+ OUString sTemp;
+ sal_Int32 nOldIndex;
+ FolderHash::iterator aIter;
+
+ sal_Int32 nIndex = aName.getLength();
+
+ if (aName == "/")
+ // root directory
+ return true;
+
+ try
+ {
+ OUString sDirName;
+ sal_Int32 nStreamIndex;
+ nStreamIndex = aName.lastIndexOf ( '/' );
+ bool bFolder = nStreamIndex == nIndex-1;
+ if ( nStreamIndex != -1 )
+ {
+ sDirName = aName.copy ( 0, nStreamIndex );
+ aIter = m_aRecent.find ( sDirName );
+ if ( aIter != m_aRecent.end() )
+ {
+ if ( bFolder )
+ {
+ sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
+ sTemp = aName.copy ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
+ if ( sTemp == ( *aIter ).second->getName() )
+ return true;
+ else
+ m_aRecent.erase ( aIter );
+ }
+ else
+ {
+ sTemp = aName.copy ( nStreamIndex + 1 );
+ if ( ( *aIter ).second->hasByName( sTemp ) )
+ return true;
+ else
+ m_aRecent.erase( aIter );
+ }
+ }
+ }
+ else
+ {
+ if ( m_xRootFolder->hasByName ( aName ) )
+ return true;
+ }
+ ZipPackageFolder * pCurrent = m_xRootFolder.get();
+ ZipPackageFolder * pPrevious = nullptr;
+ nOldIndex = 0;
+ while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
+ {
+ sTemp = aName.copy ( nOldIndex, nIndex - nOldIndex );
+ if ( nIndex == nOldIndex )
+ break;
+ if ( pCurrent->hasByName( sTemp ) )
+ {
+ pPrevious = pCurrent;
+ ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
+ if (!rInfo.bFolder)
+ throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
+ pCurrent = rInfo.pFolder;
+ }
+ else
+ return false;
+ nOldIndex = nIndex+1;
+ }
+ if ( bFolder )
+ {
+ m_aRecent[sDirName] = pPrevious;
+ return true;
+ }
+ else
+ {
+ sTemp = aName.copy( nOldIndex );
+
+ if ( pCurrent->hasByName( sTemp ) )
+ {
+ m_aRecent[sDirName] = pCurrent;
+ return true;
+ }
+ }
+ }
+ catch (const uno::RuntimeException &)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ uno::Any e(::cppu::getCaughtException());
+ throw lang::WrappedTargetRuntimeException("ZipPackage::hasByHierarchicalName", nullptr, e);
+ }
+ return false;
+}
+
+uno::Reference< XInterface > SAL_CALL ZipPackage::createInstance()
+{
+ uno::Reference < XInterface > xRef = *( new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert ) );
+ return xRef;
+}
+
+uno::Reference< XInterface > SAL_CALL ZipPackage::createInstanceWithArguments( const uno::Sequence< Any >& aArguments )
+{
+ bool bArg = false;
+ uno::Reference < XInterface > xRef;
+ if ( aArguments.hasElements() )
+ aArguments[0] >>= bArg;
+ if ( bArg )
+ xRef = *new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
+ else
+ xRef = *new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
+
+ return xRef;
+}
+
+void ZipPackage::WriteMimetypeMagicFile( ZipOutputStream& aZipOut )
+{
+ static constexpr OUString sMime (u"mimetype"_ustr);
+ if ( m_xRootFolder->hasByName( sMime ) )
+ m_xRootFolder->removeByName( sMime );
+
+ ZipEntry * pEntry = new ZipEntry;
+ sal_Int32 nBufferLength = m_xRootFolder->GetMediaType().getLength();
+ OString sMediaType = OUStringToOString( m_xRootFolder->GetMediaType(), RTL_TEXTENCODING_ASCII_US );
+ const uno::Sequence< sal_Int8 > aType( reinterpret_cast<sal_Int8 const *>(sMediaType.getStr()),
+ nBufferLength );
+
+ pEntry->sPath = sMime;
+ pEntry->nMethod = STORED;
+ pEntry->nSize = pEntry->nCompressedSize = nBufferLength;
+ pEntry->nTime = ZipOutputStream::getCurrentDosTime();
+
+ CRC32 aCRC32;
+ aCRC32.update( aType );
+ pEntry->nCrc = aCRC32.getValue();
+
+ try
+ {
+ ZipOutputStream::setEntry(pEntry);
+ aZipOut.writeLOC(pEntry);
+ aZipOut.rawWrite(aType);
+ aZipOut.rawCloseEntry();
+ }
+ catch ( const css::io::IOException & )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(
+ THROW_WHERE "Error adding mimetype to the ZipOutputStream!",
+ getXWeak(),
+ anyEx );
+ }
+}
+
+void ZipPackage::WriteManifest( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
+{
+ // Write the manifest
+ uno::Reference < XManifestWriter > xWriter = ManifestWriter::create( m_xContext );
+ ZipEntry * pEntry = new ZipEntry;
+ rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;
+
+ pEntry->sPath = "META-INF/manifest.xml";
+ pEntry->nMethod = DEFLATED;
+ pEntry->nCrc = -1;
+ pEntry->nSize = pEntry->nCompressedSize = -1;
+ pEntry->nTime = ZipOutputStream::getCurrentDosTime();
+
+ xWriter->writeManifestSequence ( pBuffer, comphelper::containerToSequence(aManList) );
+
+ sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
+ pBuffer->realloc( nBufferLength );
+
+ // the manifest.xml is never encrypted - so pass an empty reference
+ ZipOutputStream::setEntry(pEntry);
+ aZipOut.writeLOC(pEntry);
+ ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, *pEntry, nullptr, /*bEncrypt*/false);
+ aZipEntry.write(pBuffer->getSequence());
+ aZipEntry.closeEntry();
+ aZipOut.rawCloseEntry();
+}
+
+void ZipPackage::WriteContentTypes( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
+{
+ ZipEntry* pEntry = new ZipEntry;
+ rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;
+
+ pEntry->sPath = "[Content_Types].xml";
+ pEntry->nMethod = DEFLATED;
+ pEntry->nCrc = -1;
+ pEntry->nSize = pEntry->nCompressedSize = -1;
+ pEntry->nTime = ZipOutputStream::getCurrentDosTime();
+
+ // Add default entries, the count must be updated manually when appending.
+ // Add at least the standard default entries.
+ uno::Sequence< beans::StringPair > aDefaultsSequence
+ {
+ { "xml", "application/xml" },
+ { "rels", "application/vnd.openxmlformats-package.relationships+xml" },
+ { "png", "image/png" },
+ { "jpeg", "image/jpeg" }
+ };
+
+ uno::Sequence< beans::StringPair > aOverridesSequence(aManList.size());
+ auto aOverridesSequenceRange = asNonConstRange(aOverridesSequence);
+ sal_Int32 nOverSeqLength = 0;
+ for (const auto& rMan : aManList)
+ {
+ OUString aType;
+ OSL_ENSURE( rMan[PKG_MNFST_MEDIATYPE].Name == "MediaType" && rMan[PKG_MNFST_FULLPATH].Name == "FullPath",
+ "The mediatype sequence format is wrong!" );
+ rMan[PKG_MNFST_MEDIATYPE].Value >>= aType;
+ if ( !aType.isEmpty() )
+ {
+ OUString aPath;
+ // only nonempty type makes sense here
+ rMan[PKG_MNFST_FULLPATH].Value >>= aPath;
+ //FIXME: For now we have no way of differentiating defaults from others.
+ aOverridesSequenceRange[nOverSeqLength].First = "/" + aPath;
+ aOverridesSequenceRange[nOverSeqLength].Second = aType;
+ ++nOverSeqLength;
+ }
+ }
+ aOverridesSequence.realloc(nOverSeqLength);
+
+ ::comphelper::OFOPXMLHelper::WriteContentSequence(
+ pBuffer, aDefaultsSequence, aOverridesSequence, m_xContext );
+
+ sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
+ pBuffer->realloc( nBufferLength );
+
+ // there is no encryption in this format currently
+ ZipOutputStream::setEntry(pEntry);
+ aZipOut.writeLOC(pEntry);
+ ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, *pEntry, nullptr, /*bEncrypt*/false);
+ aZipEntry.write(pBuffer->getSequence());
+ aZipEntry.closeEntry();
+ aZipOut.rawCloseEntry();
+}
+
+void ZipPackage::ConnectTo( const uno::Reference< io::XInputStream >& xInStream )
+{
+ m_xContentSeek.set( xInStream, uno::UNO_QUERY_THROW );
+ m_xContentStream = xInStream;
+
+ // seek back to the beginning of the temp file so we can read segments from it
+ m_xContentSeek->seek( 0 );
+ if ( m_pZipFile )
+ m_pZipFile->setInputStream( m_xContentStream );
+ else
+ m_pZipFile.emplace(m_aMutexHolder, m_xContentStream, m_xContext, false);
+}
+
+namespace
+{
+ class RandomPool
+ {
+ private:
+ rtlRandomPool m_aRandomPool;
+ public:
+ RandomPool() : m_aRandomPool(rtl_random_createPool ())
+ {
+ }
+ rtlRandomPool get()
+ {
+ return m_aRandomPool;
+ }
+ ~RandomPool()
+ {
+ // Clean up random pool memory
+ rtl_random_destroyPool(m_aRandomPool);
+ }
+ };
+}
+
+uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
+{
+ // In case the target local file does not exist or empty
+ // write directly to it otherwise create a temporary file to write to.
+ // If a temporary file is created it is returned back by the method.
+ // If the data written directly, xComponentStream will be switched here
+
+ bool bUseTemp = true;
+ uno::Reference < io::XInputStream > xResult;
+ uno::Reference < io::XInputStream > xTempIn;
+
+ uno::Reference < io::XOutputStream > xTempOut;
+ uno::Reference< io::XActiveDataStreamer > xSink;
+
+ if ( m_eMode == e_IMode_URL && !m_pZipFile && isLocalFile() )
+ {
+ xSink = openOriginalForOutput();
+ if( xSink.is() )
+ {
+ uno::Reference< io::XStream > xStr = xSink->getStream();
+ if( xStr.is() )
+ {
+ xTempOut = xStr->getOutputStream();
+ if( xTempOut.is() )
+ bUseTemp = false;
+ }
+ }
+ }
+ else if ( m_eMode == e_IMode_XStream && !m_pZipFile )
+ {
+ // write directly to an empty stream
+ xTempOut = m_xStream->getOutputStream();
+ if( xTempOut.is() )
+ bUseTemp = false;
+ }
+
+ if( bUseTemp )
+ {
+ // create temporary file
+ rtl::Reference < utl::TempFileFastService > xTempFile( new utl::TempFileFastService );
+ xTempOut.set( xTempFile );
+ xTempIn.set( xTempFile );
+ }
+
+ // Hand it to the ZipOutputStream:
+ ZipOutputStream aZipOut( xTempOut );
+ try
+ {
+ if ( m_nFormat == embed::StorageFormats::PACKAGE )
+ {
+ // Remove the old manifest.xml file as the
+ // manifest will be re-generated and the
+ // META-INF directory implicitly created if does not exist
+ static constexpr OUString sMeta (u"META-INF"_ustr);
+
+ if ( m_xRootFolder->hasByName( sMeta ) )
+ {
+ static constexpr OUString sManifest (u"manifest.xml"_ustr);
+
+ uno::Reference< XNameContainer > xMetaInfFolder;
+ Any aAny = m_xRootFolder->getByName( sMeta );
+ aAny >>= xMetaInfFolder;
+ if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
+ xMetaInfFolder->removeByName( sManifest );
+ }
+
+ // Write a magic file with mimetype
+ WriteMimetypeMagicFile( aZipOut );
+ }
+ else if ( m_nFormat == embed::StorageFormats::OFOPXML )
+ {
+ // Remove the old [Content_Types].xml file as the
+ // file will be re-generated
+
+ static constexpr OUString aContentTypes(u"[Content_Types].xml"_ustr);
+
+ if ( m_xRootFolder->hasByName( aContentTypes ) )
+ m_xRootFolder->removeByName( aContentTypes );
+ }
+
+ // Create a vector to store data for the manifest.xml file
+ std::vector < uno::Sequence < PropertyValue > > aManList;
+
+ static constexpr OUStringLiteral sMediaType(u"MediaType");
+ static constexpr OUStringLiteral sVersion(u"Version");
+ static constexpr OUStringLiteral sFullPath(u"FullPath");
+ const bool bIsGpgEncrypt = m_aGpgProps.hasElements();
+
+ // note: this is always created here (needed for GPG), possibly
+ // filtered out later in ManifestExport
+ if ( m_nFormat == embed::StorageFormats::PACKAGE )
+ {
+ uno::Sequence < PropertyValue > aPropSeq(
+ bIsGpgEncrypt ? PKG_SIZE_NOENCR_MNFST+1 : PKG_SIZE_NOENCR_MNFST );
+ auto pPropSeq = aPropSeq.getArray();
+ pPropSeq [PKG_MNFST_MEDIATYPE].Name = sMediaType;
+ pPropSeq [PKG_MNFST_MEDIATYPE].Value <<= m_xRootFolder->GetMediaType();
+ pPropSeq [PKG_MNFST_VERSION].Name = sVersion;
+ pPropSeq [PKG_MNFST_VERSION].Value <<= m_xRootFolder->GetVersion();
+ pPropSeq [PKG_MNFST_FULLPATH].Name = sFullPath;
+ pPropSeq [PKG_MNFST_FULLPATH].Value <<= OUString("/");
+
+ if( bIsGpgEncrypt )
+ {
+ pPropSeq[PKG_SIZE_NOENCR_MNFST].Name = "KeyInfo";
+ pPropSeq[PKG_SIZE_NOENCR_MNFST].Value <<= m_aGpgProps;
+ }
+ aManList.push_back( aPropSeq );
+ }
+
+ {
+ // This will be used to generate random salt and initialisation vectors
+ // for encrypted streams
+ RandomPool aRandomPool;
+
+ ::std::optional<sal_Int32> oPBKDF2IterationCount;
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args;
+
+ if (!bIsGpgEncrypt)
+ {
+ if (m_nKeyDerivationFunctionID == xml::crypto::KDFID::PBKDF2)
+ { // if there is only one KDF invocation, increase the safety margin
+ oPBKDF2IterationCount.emplace(officecfg::Office::Common::Misc::ExperimentalMode::get() ? 600000 : 100000);
+ }
+ else
+ {
+ assert(m_nKeyDerivationFunctionID == xml::crypto::KDFID::Argon2id);
+ oArgon2Args.emplace(3, (1<<16), 4);
+ }
+ }
+
+ // call saveContents - it will recursively save sub-directories
+ m_xRootFolder->saveContents("", aManList, aZipOut, GetEncryptionKey(),
+ oPBKDF2IterationCount, oArgon2Args, aRandomPool.get());
+ }
+
+ if( m_nFormat == embed::StorageFormats::PACKAGE )
+ {
+ WriteManifest( aZipOut, aManList );
+ }
+ else if( m_nFormat == embed::StorageFormats::OFOPXML )
+ {
+ WriteContentTypes( aZipOut, aManList );
+ }
+
+ aZipOut.finish();
+
+ if( bUseTemp )
+ xResult = xTempIn;
+
+ // Update our References to point to the new temp file
+ if( !bUseTemp )
+ {
+ // the case when the original contents were written directly
+ xTempOut->flush();
+
+ // in case the stream is based on a file it will implement the following interface
+ // the call should be used to be sure that the contents are written to the file system
+ uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( xTempOut, uno::UNO_QUERY );
+ if (asyncOutputMonitor.is() && !m_bDisableFileSync)
+ asyncOutputMonitor->waitForCompletion();
+
+ // no need to postpone switching to the new stream since the target was written directly
+ uno::Reference< io::XInputStream > xNewStream;
+ if ( m_eMode == e_IMode_URL )
+ xNewStream = xSink->getStream()->getInputStream();
+ else if ( m_eMode == e_IMode_XStream && m_xStream.is() )
+ xNewStream = m_xStream->getInputStream();
+
+ if ( xNewStream.is() )
+ ConnectTo( xNewStream );
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ if( bUseTemp )
+ {
+ // no information loss appears, thus no special handling is required
+ uno::Any aCaught( ::cppu::getCaughtException() );
+
+ // it is allowed to throw WrappedTargetException
+ WrappedTargetException aException;
+ if ( aCaught >>= aException )
+ throw aException;
+
+ throw WrappedTargetException(
+ THROW_WHERE "Problem writing the original content!",
+ getXWeak(),
+ aCaught );
+ }
+ else
+ {
+ // the document is written directly, although it was empty it is important to notify that the writing has failed
+ // TODO/LATER: let the package be able to recover in this situation
+ OUString aErrTxt(THROW_WHERE "This package is unusable!");
+ embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), OUString() );
+ throw WrappedTargetException( aErrTxt,
+ getXWeak(),
+ Any ( aException ) );
+ }
+ }
+
+ return xResult;
+}
+
+uno::Reference< XActiveDataStreamer > ZipPackage::openOriginalForOutput()
+{
+ // open and truncate the original file
+ Content aOriginalContent(
+ m_aURL, uno::Reference< XCommandEnvironment >(),
+ m_xContext );
+ uno::Reference< XActiveDataStreamer > xSink = new ActiveDataStreamer;
+
+ if ( m_eMode == e_IMode_URL )
+ {
+ try
+ {
+ bool bTruncSuccess = false;
+
+ try
+ {
+ Exception aDetect;
+ Any aAny = aOriginalContent.setPropertyValue("Size", Any( sal_Int64(0) ) );
+ if( !( aAny >>= aDetect ) )
+ bTruncSuccess = true;
+ }
+ catch( Exception& )
+ {
+ }
+
+ if( !bTruncSuccess )
+ {
+ // the file is not accessible
+ // just try to write an empty stream to it
+
+ uno::Reference< XInputStream > xTempIn = new DummyInputStream; //uno::Reference< XInputStream >( xTempOut, UNO_QUERY );
+ aOriginalContent.writeStream( xTempIn , true );
+ }
+
+ OpenCommandArgument2 aArg;
+ aArg.Mode = OpenMode::DOCUMENT;
+ aArg.Priority = 0; // unused
+ aArg.Sink = xSink;
+ aArg.Properties = uno::Sequence< Property >( 0 ); // unused
+
+ aOriginalContent.executeCommand("open", Any( aArg ) );
+ }
+ catch( Exception& )
+ {
+ // seems to be nonlocal file
+ // temporary file mechanics should be used
+ }
+ }
+
+ return xSink;
+}
+
+void SAL_CALL ZipPackage::commitChanges()
+{
+ // lock the component for the time of committing
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_eMode == e_IMode_XInputStream )
+ {
+ IOException aException;
+ throw WrappedTargetException(THROW_WHERE "This package is read only!",
+ getXWeak(), Any ( aException ) );
+ }
+ // first the writeTempFile is called, if it returns a stream the stream should be written to the target
+ // if no stream was returned, the file was written directly, nothing should be done
+ uno::Reference< io::XInputStream > xTempInStream;
+ try
+ {
+ xTempInStream = writeTempFile();
+ }
+ catch (const ucb::ContentCreationException&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(THROW_WHERE "Temporary file should be creatable!",
+ getXWeak(), anyEx );
+ }
+ if ( xTempInStream.is() )
+ {
+ uno::Reference< io::XSeekable > xTempSeek( xTempInStream, uno::UNO_QUERY_THROW );
+
+ try
+ {
+ xTempSeek->seek( 0 );
+ }
+ catch( const uno::Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(THROW_WHERE "Temporary file should be seekable!",
+ getXWeak(), anyEx );
+ }
+
+ try
+ {
+ // connect to the temporary stream
+ ConnectTo( xTempInStream );
+ }
+ catch( const io::IOException& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(THROW_WHERE "Temporary file should be connectable!",
+ getXWeak(), anyEx );
+ }
+
+ if ( m_eMode == e_IMode_XStream )
+ {
+ // First truncate our output stream
+ uno::Reference < XOutputStream > xOutputStream;
+
+ // preparation for copy step
+ try
+ {
+ xOutputStream = m_xStream->getOutputStream();
+
+ // Make sure we avoid a situation where the current position is
+ // not zero, but the underlying file is truncated in the
+ // meantime.
+ uno::Reference<io::XSeekable> xSeekable(xOutputStream, uno::UNO_QUERY);
+ if (xSeekable.is())
+ xSeekable->seek(0);
+
+ uno::Reference < XTruncate > xTruncate ( xOutputStream, UNO_QUERY_THROW );
+
+ // after successful truncation the original file contents are already lost
+ xTruncate->truncate();
+ }
+ catch( const uno::Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(THROW_WHERE "This package is read only!",
+ getXWeak(), anyEx );
+ }
+
+ try
+ {
+ // then copy the contents of the tempfile to our output stream
+ ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, xOutputStream );
+ xOutputStream->flush();
+ uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor(
+ xOutputStream, uno::UNO_QUERY );
+ if ( asyncOutputMonitor.is() ) {
+ asyncOutputMonitor->waitForCompletion();
+ }
+ }
+ catch( uno::Exception& )
+ {
+ // if anything goes wrong in this block the target file becomes corrupted
+ // so an exception should be thrown as a notification about it
+ // and the package must disconnect from the stream
+ DisconnectFromTargetAndThrowException_Impl( xTempInStream );
+ }
+ }
+ else if ( m_eMode == e_IMode_URL )
+ {
+ uno::Reference< XOutputStream > aOrigFileStream;
+ bool bCanBeCorrupted = false;
+
+ if( isLocalFile() )
+ {
+ // write directly in case of local file
+ uno::Reference< css::ucb::XSimpleFileAccess3 > xSimpleAccess(
+ SimpleFileAccess::create( m_xContext ) );
+ OSL_ENSURE( xSimpleAccess.is(), "Can't instantiate SimpleFileAccess service!" );
+ uno::Reference< io::XTruncate > xOrigTruncate;
+ if ( xSimpleAccess.is() )
+ {
+ try
+ {
+ aOrigFileStream = xSimpleAccess->openFileWrite( m_aURL );
+ xOrigTruncate.set( aOrigFileStream, uno::UNO_QUERY_THROW );
+ // after successful truncation the file is already corrupted
+ xOrigTruncate->truncate();
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ if( xOrigTruncate.is() )
+ {
+ try
+ {
+ ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, aOrigFileStream );
+ aOrigFileStream->closeOutput();
+ }
+ catch( uno::Exception& )
+ {
+ try {
+ aOrigFileStream->closeOutput();
+ } catch ( uno::Exception& ) {}
+
+ aOrigFileStream.clear();
+ // the original file can already be corrupted
+ bCanBeCorrupted = true;
+ }
+ }
+ }
+
+ if( !aOrigFileStream.is() )
+ {
+ try
+ {
+ uno::Reference < XPropertySet > xPropSet ( xTempInStream, UNO_QUERY_THROW );
+
+ OUString sTargetFolder = m_aURL.copy ( 0, m_aURL.lastIndexOf ( u'/' ) );
+ Content aContent(
+ sTargetFolder, uno::Reference< XCommandEnvironment >(),
+ m_xContext );
+
+ OUString sTempURL;
+ Any aAny = xPropSet->getPropertyValue ("Uri");
+ aAny >>= sTempURL;
+
+ TransferInfo aInfo;
+ aInfo.NameClash = NameClash::OVERWRITE;
+ aInfo.MoveData = false;
+ aInfo.SourceURL = sTempURL;
+ aInfo.NewTitle = rtl::Uri::decode ( m_aURL.copy ( 1 + m_aURL.lastIndexOf ( u'/' ) ),
+ rtl_UriDecodeWithCharset,
+ RTL_TEXTENCODING_UTF8 );
+ // if the file is still not corrupted, it can become after the next step
+ aContent.executeCommand ("transfer", Any(aInfo) );
+ }
+ catch ( const css::uno::Exception& )
+ {
+ if ( bCanBeCorrupted )
+ DisconnectFromTargetAndThrowException_Impl( xTempInStream );
+
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(
+ THROW_WHERE "This package may be read only!",
+ getXWeak(),
+ anyEx );
+ }
+ }
+ }
+ }
+
+ // after successful storing it can be set to false
+ m_bMediaTypeFallbackUsed = false;
+}
+
+void ZipPackage::DisconnectFromTargetAndThrowException_Impl( const uno::Reference< io::XInputStream >& xTempStream )
+{
+ m_xStream.set( xTempStream, uno::UNO_QUERY );
+ if ( m_xStream.is() )
+ m_eMode = e_IMode_XStream;
+ else
+ m_eMode = e_IMode_XInputStream;
+
+ OUString aTempURL;
+ try {
+ uno::Reference< beans::XPropertySet > xTempFile( xTempStream, uno::UNO_QUERY_THROW );
+ uno::Any aUrl = xTempFile->getPropertyValue("Uri");
+ aUrl >>= aTempURL;
+ xTempFile->setPropertyValue("RemoveFile",
+ uno::Any( false ) );
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL( "These calls are pretty simple, they should not fail!" );
+ }
+
+ OUString aErrTxt(THROW_WHERE "This package is read only!");
+ embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), aTempURL );
+ throw WrappedTargetException( aErrTxt,
+ getXWeak(),
+ Any ( aException ) );
+}
+
+uno::Sequence< sal_Int8 > ZipPackage::GetEncryptionKey()
+{
+ uno::Sequence< sal_Int8 > aResult;
+
+ if ( m_aStorageEncryptionKeys.hasElements() )
+ {
+ OUString aNameToFind;
+ if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA256 )
+ aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
+ else if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA1 )
+ aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT;
+ else
+ throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
+
+ for ( const auto& rKey : std::as_const(m_aStorageEncryptionKeys) )
+ if ( rKey.Name == aNameToFind )
+ rKey.Value >>= aResult;
+ }
+ else
+ aResult = m_aEncryptionKey;
+
+ return aResult;
+}
+
+sal_Bool SAL_CALL ZipPackage::hasPendingChanges()
+{
+ return false;
+}
+Sequence< ElementChange > SAL_CALL ZipPackage::getPendingChanges()
+{
+ return uno::Sequence < ElementChange > ();
+}
+
+
+OUString ZipPackage::getImplementationName()
+{
+ return "com.sun.star.packages.comp.ZipPackage";
+}
+
+Sequence< OUString > ZipPackage::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.Package" };
+}
+
+sal_Bool SAL_CALL ZipPackage::supportsService( OUString const & rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Reference< XPropertySetInfo > SAL_CALL ZipPackage::getPropertySetInfo()
+{
+ return uno::Reference < XPropertySetInfo > ();
+}
+
+void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ throw UnknownPropertyException(aPropertyName);
+
+ if (aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY
+ ||aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY
+ ||aPropertyName == IS_INCONSISTENT_PROPERTY
+ ||aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY)
+ throw PropertyVetoException(THROW_WHERE );
+ else if ( aPropertyName == ENCRYPTION_KEY_PROPERTY )
+ {
+ if ( !( aValue >>= m_aEncryptionKey ) )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
+
+ m_aStorageEncryptionKeys.realloc( 0 );
+ }
+ else if ( aPropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
+ {
+ // this property is only necessary to support raw passwords in storage API;
+ // because of this support the storage has to operate with more than one key dependent on storage generation algorithm;
+ // when this support is removed, the storage will get only one key from outside
+ if ( !( aValue >>= m_aStorageEncryptionKeys ) )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
+
+ m_aEncryptionKey.realloc( 0 );
+ }
+ else if ( aPropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
+ {
+ uno::Sequence< beans::NamedValue > aAlgorithms;
+ if ( m_pZipFile || !( aValue >>= aAlgorithms ) || !aAlgorithms.hasElements() )
+ {
+ // the algorithms can not be changed if the file has a persistence based on the algorithms ( m_pZipFile )
+ throw IllegalArgumentException(THROW_WHERE "unexpected algorithms list is provided.", uno::Reference< uno::XInterface >(), 2 );
+ }
+
+ for ( const auto& rAlgorithm : std::as_const(aAlgorithms) )
+ {
+ if ( rAlgorithm.Name == "StartKeyGenerationAlgorithm" )
+ {
+ sal_Int32 nID = 0;
+ if ( !( rAlgorithm.Value >>= nID )
+ || ( nID != xml::crypto::DigestID::SHA256 && nID != xml::crypto::DigestID::SHA1 ) )
+ {
+ throw IllegalArgumentException(THROW_WHERE "Unexpected start key generation algorithm is provided!", uno::Reference<uno::XInterface>(), 2);
+ }
+
+ m_nStartKeyGenerationID = nID;
+ }
+ else if (rAlgorithm.Name == "KeyDerivationFunction")
+ {
+ sal_Int32 nID = 0;
+ if (!(rAlgorithm.Value >>= nID)
+ || (nID != xml::crypto::KDFID::PBKDF2
+ && nID != xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P
+ && nID != xml::crypto::KDFID::Argon2id))
+ {
+ throw IllegalArgumentException(THROW_WHERE "Unexpected key derivation function provided!", uno::Reference<uno::XInterface>(), 2);
+ }
+ m_nKeyDerivationFunctionID = nID;
+ }
+ else if ( rAlgorithm.Name == "EncryptionAlgorithm" )
+ {
+ sal_Int32 nID = 0;
+ if ( !( rAlgorithm.Value >>= nID )
+ || (nID != xml::crypto::CipherID::AES_GCM_W3C
+ && nID != xml::crypto::CipherID::AES_CBC_W3C_PADDING
+ && nID != xml::crypto::CipherID::BLOWFISH_CFB_8))
+ {
+ throw IllegalArgumentException(THROW_WHERE "Unexpected encryption algorithm is provided!", uno::Reference<uno::XInterface>(), 2);
+ }
+
+ m_nCommonEncryptionID = nID;
+ }
+ else if ( rAlgorithm.Name == "ChecksumAlgorithm" )
+ {
+ sal_Int32 nID = 0;
+ if (!rAlgorithm.Value.hasValue())
+ {
+ m_oChecksumDigestID.reset();
+ continue;
+ }
+ if ( !( rAlgorithm.Value >>= nID )
+ || ( nID != xml::crypto::DigestID::SHA1_1K && nID != xml::crypto::DigestID::SHA256_1K ) )
+ {
+ throw IllegalArgumentException(THROW_WHERE "Unexpected checksum algorithm is provided!", uno::Reference<uno::XInterface>(), 2);
+ }
+
+ m_oChecksumDigestID.emplace(nID);
+ }
+ else
+ {
+ OSL_ENSURE( false, "Unexpected encryption algorithm is provided!" );
+ throw IllegalArgumentException(THROW_WHERE "unexpected algorithms list is provided.", uno::Reference< uno::XInterface >(), 2 );
+ }
+ }
+ }
+ else if ( aPropertyName == ENCRYPTION_GPG_PROPERTIES )
+ {
+ uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProps;
+ if ( !( aValue >>= aGpgProps ) || !aGpgProps.hasElements() )
+ {
+ throw IllegalArgumentException(THROW_WHERE "unexpected Gpg properties are provided.", uno::Reference< uno::XInterface >(), 2 );
+ }
+
+ m_aGpgProps = aGpgProps;
+
+ // override algorithm defaults (which are some legacy ODF
+ // defaults) with reasonable values
+ // note: these should be overridden by SfxObjectShell::SetupStorage()
+ m_nStartKeyGenerationID = 0; // this is unused for PGP
+ m_nKeyDerivationFunctionID = xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
+ m_nCommonEncryptionID = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
+ m_oChecksumDigestID.emplace(xml::crypto::DigestID::SHA512_1K);
+ }
+ else
+ throw UnknownPropertyException(aPropertyName);
+}
+
+Any SAL_CALL ZipPackage::getPropertyValue( const OUString& PropertyName )
+{
+ // TODO/LATER: Activate the check when zip-ucp is ready
+ // if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ // throw UnknownPropertyException(THROW_WHERE );
+
+ if ( PropertyName == ENCRYPTION_KEY_PROPERTY )
+ {
+ return Any(m_aEncryptionKey);
+ }
+ else if ( PropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
+ {
+ ::comphelper::SequenceAsHashMap aAlgorithms;
+ aAlgorithms["StartKeyGenerationAlgorithm"] <<= m_nStartKeyGenerationID;
+ aAlgorithms["KeyDerivationFunction"] <<= m_nKeyDerivationFunctionID;
+ aAlgorithms["EncryptionAlgorithm"] <<= m_nCommonEncryptionID;
+ if (m_oChecksumDigestID)
+ {
+ aAlgorithms["ChecksumAlgorithm"] <<= *m_oChecksumDigestID;
+ }
+ else
+ {
+ aAlgorithms["ChecksumAlgorithm"];
+ }
+ return Any(aAlgorithms.getAsConstNamedValueList());
+ }
+ if ( PropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
+ {
+ return Any(m_aStorageEncryptionKeys);
+ }
+ else if ( PropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY )
+ {
+ return Any(m_bHasEncryptedEntries);
+ }
+ else if ( PropertyName == ENCRYPTION_GPG_PROPERTIES )
+ {
+ return Any(m_aGpgProps);
+ }
+ else if ( PropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY )
+ {
+ return Any(m_bHasNonEncryptedEntries);
+ }
+ else if ( PropertyName == IS_INCONSISTENT_PROPERTY )
+ {
+ return Any(m_bInconsistent);
+ }
+ else if ( PropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY )
+ {
+ 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*/ )
+{
+}
+void SAL_CALL ZipPackage::removePropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*aListener*/ )
+{
+}
+void SAL_CALL ZipPackage::addVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
+{
+}
+void SAL_CALL ZipPackage::removeVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+package_ZipPackage_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ZipPackage(context));
+}
+
+extern "C" bool TestImportZip(SvStream& rStream)
+{
+ // explicitly tests the "RepairPackage" recovery mode
+ rtl::Reference<ZipPackage> xPackage(new ZipPackage(comphelper::getProcessComponentContext()));
+ css::uno::Reference<css::io::XInputStream> xStream(new utl::OInputStreamWrapper(rStream));
+ css::uno::Sequence<Any> aArgs{ Any(xStream), Any(NamedValue("RepairPackage", Any(true))) };
+ xPackage->initialize(aArgs);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageBuffer.cxx b/package/source/zippackage/ZipPackageBuffer.cxx
new file mode 100644
index 0000000000..810daa737a
--- /dev/null
+++ b/package/source/zippackage/ZipPackageBuffer.cxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipPackageBuffer.hxx>
+#include <PackageConstants.hxx>
+#include <string.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace ::com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+using com::sun::star::lang::IllegalArgumentException;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageBuffer::ZipPackageBuffer()
+: m_nBufferSize (n_ConstBufferSize)
+, m_nEnd(0)
+, m_nCurrent(0)
+, m_bMustInitBuffer ( true )
+{
+}
+ZipPackageBuffer::~ZipPackageBuffer()
+{
+}
+
+sal_Int32 SAL_CALL ZipPackageBuffer::readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ if (nBytesToRead < 0)
+ throw BufferSizeExceededException(THROW_WHERE, *this );
+
+ if (nBytesToRead + m_nCurrent > m_nEnd)
+ nBytesToRead = static_cast < sal_Int32 > (m_nEnd - m_nCurrent);
+
+ aData.realloc ( nBytesToRead );
+ memcpy(aData.getArray(), m_aBuffer.getConstArray() + m_nCurrent, nBytesToRead);
+ m_nCurrent +=nBytesToRead;
+ return nBytesToRead;
+}
+
+sal_Int32 SAL_CALL ZipPackageBuffer::readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ return readBytes(aData, nMaxBytesToRead);
+}
+void SAL_CALL ZipPackageBuffer::skipBytes( sal_Int32 nBytesToSkip )
+{
+ if (nBytesToSkip < 0)
+ throw BufferSizeExceededException(THROW_WHERE, *this );
+
+ if (nBytesToSkip + m_nCurrent > m_nEnd)
+ nBytesToSkip = static_cast < sal_Int32 > (m_nEnd - m_nCurrent);
+
+ m_nCurrent+=nBytesToSkip;
+}
+sal_Int32 SAL_CALL ZipPackageBuffer::available( )
+{
+ return std::min<sal_Int64>(SAL_MAX_INT32, m_nEnd - m_nCurrent);
+}
+void SAL_CALL ZipPackageBuffer::closeInput( )
+{
+}
+void SAL_CALL ZipPackageBuffer::writeBytes( const Sequence< sal_Int8 >& aData )
+{
+ sal_Int64 nDataLen = aData.getLength(), nCombined = m_nEnd + nDataLen;
+
+ if ( nCombined > m_nBufferSize)
+ {
+ do
+ m_nBufferSize *=2;
+ while (nCombined > m_nBufferSize);
+ m_aBuffer.realloc(static_cast < sal_Int32 > (m_nBufferSize));
+ m_bMustInitBuffer = false;
+ }
+ else if (m_bMustInitBuffer)
+ {
+ m_aBuffer.realloc ( static_cast < sal_Int32 > ( m_nBufferSize ) );
+ m_bMustInitBuffer = false;
+ }
+ memcpy( m_aBuffer.getArray() + m_nCurrent, aData.getConstArray(), static_cast < sal_Int32 > (nDataLen));
+ m_nCurrent+=nDataLen;
+ if (m_nCurrent>m_nEnd)
+ m_nEnd = m_nCurrent;
+}
+void SAL_CALL ZipPackageBuffer::flush( )
+{
+}
+void SAL_CALL ZipPackageBuffer::closeOutput( )
+{
+}
+void SAL_CALL ZipPackageBuffer::seek( sal_Int64 location )
+{
+ if ( location > m_nEnd || location < 0 )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+ m_nCurrent = location;
+}
+sal_Int64 SAL_CALL ZipPackageBuffer::getPosition( )
+{
+ return m_nCurrent;
+}
+sal_Int64 SAL_CALL ZipPackageBuffer::getLength( )
+{
+ return m_nEnd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageEntry.cxx b/package/source/zippackage/ZipPackageEntry.cxx
new file mode 100644
index 0000000000..56337109ec
--- /dev/null
+++ b/package/source/zippackage/ZipPackageEntry.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipPackageEntry.hxx>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <ZipPackageFolder.hxx>
+
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageEntry::ZipPackageEntry()
+: mbIsFolder( false )
+, mbAllowRemoveOnInsert(false)
+, mpParent ( nullptr )
+, m_nFormat(0)
+{
+}
+
+ZipPackageEntry::~ZipPackageEntry()
+{
+ // When the entry is destroyed it must be already disconnected from the parent
+ OSL_ENSURE( !mpParent, "The parent must be disconnected already! Memory corruption is possible!" );
+}
+
+// XChild
+OUString SAL_CALL ZipPackageEntry::getName( )
+{
+ return msName;
+}
+void SAL_CALL ZipPackageEntry::setName( const OUString& aName )
+{
+ if ( mpParent && !msName.isEmpty() && mpParent->hasByName ( msName ) )
+ mpParent->removeByName ( msName );
+
+ // unfortunately no other exception than RuntimeException can be thrown here
+ // usually the package is used through storage implementation, the problem should be detected there
+ if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aName, true ) )
+ throw RuntimeException(THROW_WHERE "Unexpected character is used in file name." );
+
+ msName = aName;
+
+ if ( mpParent )
+ mpParent->doInsertByName ( this, false );
+}
+uno::Reference< XInterface > SAL_CALL ZipPackageEntry::getParent( )
+{
+ // return uno::Reference< XInterface >( xParent, UNO_QUERY );
+ return cppu::getXWeak( mpParent );
+}
+
+void ZipPackageEntry::doSetParent ( ZipPackageFolder * pNewParent )
+{
+ // xParent = mpParent = pNewParent;
+ mpParent = pNewParent;
+ if ( !msName.isEmpty() && !pNewParent->hasByName ( msName ) )
+ pNewParent->doInsertByName ( this, false );
+}
+
+void SAL_CALL ZipPackageEntry::setParent( const uno::Reference< XInterface >& xNewParent )
+{
+ if ( !xNewParent.is() )
+ throw NoSupportException(THROW_WHERE );
+ ZipPackageFolder* pNewParent = dynamic_cast<ZipPackageFolder*>(xNewParent.get());
+ if (!pNewParent)
+ throw NoSupportException(THROW_WHERE );
+
+ if ( pNewParent != mpParent )
+ {
+ if ( mpParent && !msName.isEmpty() && mpParent->hasByName ( msName ) && mbAllowRemoveOnInsert )
+ mpParent->removeByName( msName );
+ doSetParent ( pNewParent );
+ }
+}
+ //XPropertySet
+uno::Reference< beans::XPropertySetInfo > SAL_CALL ZipPackageEntry::getPropertySetInfo( )
+{
+ return uno::Reference < beans::XPropertySetInfo > ();
+}
+void SAL_CALL ZipPackageEntry::addPropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+{
+}
+void SAL_CALL ZipPackageEntry::removePropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ )
+{
+}
+void SAL_CALL ZipPackageEntry::addVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+void SAL_CALL ZipPackageEntry::removeVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageFolder.cxx b/package/source/zippackage/ZipPackageFolder.cxx
new file mode 100644
index 0000000000..bca4e46e1b
--- /dev/null
+++ b/package/source/zippackage/ZipPackageFolder.cxx
@@ -0,0 +1,425 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipPackageFolder.hxx>
+#include <ZipOutputStream.hxx>
+#include <ZipPackageStream.hxx>
+#include <PackageConstants.hxx>
+#include "ZipPackageFolderEnumeration.hxx"
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/packages/zip/ZipException.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+using namespace com::sun::star;
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::packages;
+using namespace com::sun::star::container;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::io;
+using namespace cppu;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageFolder::ZipPackageFolder( const css::uno::Reference < css::uno::XComponentContext >& xContext,
+ sal_Int32 nFormat,
+ bool bAllowRemoveOnInsert )
+{
+ m_xContext = xContext;
+ m_nFormat = nFormat;
+ mbAllowRemoveOnInsert = bAllowRemoveOnInsert;
+ SetFolder ( true );
+ aEntry.nVersion = -1;
+ aEntry.nFlag = 0;
+ aEntry.nMethod = STORED;
+ aEntry.nTime = -1;
+ aEntry.nCrc = 0;
+ aEntry.nCompressedSize = 0;
+ aEntry.nSize = 0;
+ aEntry.nOffset = -1;
+}
+
+ZipPackageFolder::~ZipPackageFolder()
+{
+}
+
+bool ZipPackageFolder::LookForUnexpectedODF12Streams(
+ std::u16string_view const aPath, bool const isWholesomeEncryption)
+{
+ bool bHasUnexpected = false;
+
+ for (const auto& [rShortName, rInfo] : maContents)
+ {
+ if ( rInfo.bFolder )
+ {
+ if ( aPath == u"META-INF/" )
+ {
+ // META-INF is not allowed to contain subfolders
+ bHasUnexpected = true;
+ }
+ else if (isWholesomeEncryption && rShortName != u"META-INF")
+ {
+ bHasUnexpected = true;
+ }
+ else
+ {
+ OUString sOwnPath = aPath + rShortName + "/";
+ bHasUnexpected = rInfo.pFolder->LookForUnexpectedODF12Streams(sOwnPath, isWholesomeEncryption);
+ }
+ }
+ else
+ {
+ if ( aPath == u"META-INF/" )
+ {
+ if ( rShortName != "manifest.xml"
+ && rShortName.indexOf( "signatures" ) == -1 )
+ {
+ // a stream from META-INF with unexpected name
+ bHasUnexpected = true;
+ }
+
+ // streams from META-INF with expected names are allowed not to be registered in manifest.xml
+ }
+ else if (isWholesomeEncryption && rShortName != "mimetype" && rShortName != "encrypted-package")
+ {
+ bHasUnexpected = true;
+ }
+ else if ( !rInfo.pStream->IsFromManifest() )
+ {
+ // the stream is not in META-INF and is not registered in manifest.xml,
+ // check whether it is an internal part of the package format
+ if ( !aPath.empty() || rShortName != "mimetype" )
+ {
+ // if it is not "mimetype" from the root it is not a part of the package
+ bHasUnexpected = true;
+ }
+ }
+ }
+
+ if (bHasUnexpected)
+ break;
+ }
+
+ return bHasUnexpected;
+}
+
+void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair& aPair )
+{
+ OUString aExt;
+ if ( aPair.First.toChar() == '.' )
+ aExt = aPair.First;
+ else
+ aExt = "." + aPair.First;
+
+ for (const auto& [rShortName, rInfo] : maContents)
+ {
+ if ( rInfo.bFolder )
+ rInfo.pFolder->setChildStreamsTypeByExtension( aPair );
+ else
+ {
+ sal_Int32 nPathLength = rShortName.getLength();
+ sal_Int32 nExtLength = aExt.getLength();
+ if ( nPathLength >= nExtLength && rShortName.match( aExt, nPathLength - nExtLength ) )
+ rInfo.pStream->SetMediaType( aPair.Second );
+ }
+ }
+}
+
+ // XNameContainer
+void SAL_CALL ZipPackageFolder::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ if (hasByName(aName))
+ throw ElementExistException(THROW_WHERE );
+
+ uno::Reference < XInterface > xRef;
+ aElement >>= xRef;
+ if ( !(aElement >>= xRef) )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ ZipPackageEntry* pEntry = dynamic_cast<ZipPackageFolder*>(xRef.get());
+ if (!pEntry)
+ pEntry = dynamic_cast<ZipPackageStream*>(xRef.get());
+ if (!pEntry)
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ if (pEntry->getName() != aName )
+ pEntry->setName (aName);
+ doInsertByName ( pEntry, true );
+}
+
+void SAL_CALL ZipPackageFolder::removeByName( const OUString& Name )
+{
+ ContentHash::iterator aIter = maContents.find ( Name );
+ if ( aIter == maContents.end() )
+ throw NoSuchElementException(THROW_WHERE );
+ maContents.erase( aIter );
+}
+ // XEnumerationAccess
+uno::Reference< XEnumeration > SAL_CALL ZipPackageFolder::createEnumeration( )
+{
+ return uno::Reference < XEnumeration> (new ZipPackageFolderEnumeration(maContents));
+}
+ // XElementAccess
+uno::Type SAL_CALL ZipPackageFolder::getElementType( )
+{
+ return cppu::UnoType<XInterface>::get();
+}
+sal_Bool SAL_CALL ZipPackageFolder::hasElements( )
+{
+ return !maContents.empty();
+}
+ // XNameAccess
+ZipContentInfo& ZipPackageFolder::doGetByName( const OUString& aName )
+{
+ ContentHash::iterator aIter = maContents.find ( aName );
+ if ( aIter == maContents.end())
+ throw NoSuchElementException(THROW_WHERE );
+ return aIter->second;
+}
+
+uno::Any SAL_CALL ZipPackageFolder::getByName( const OUString& aName )
+{
+ return uno::Any ( uno::Reference(cppu::getXWeak(doGetByName ( aName ).xPackageEntry.get())) );
+}
+uno::Sequence< OUString > SAL_CALL ZipPackageFolder::getElementNames( )
+{
+ return comphelper::mapKeysToSequence(maContents);
+}
+sal_Bool SAL_CALL ZipPackageFolder::hasByName( const OUString& aName )
+{
+ return maContents.find ( aName ) != maContents.end ();
+}
+ // XNameReplace
+void SAL_CALL ZipPackageFolder::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ if ( !hasByName( aName ) )
+ throw NoSuchElementException(THROW_WHERE );
+
+ removeByName( aName );
+ insertByName(aName, aElement);
+}
+
+bool ZipPackageFolder::saveChild(
+ const OUString &rPath,
+ std::vector < uno::Sequence < PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ ::std::optional<sal_Int32> const oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args,
+ const rtlRandomPool &rRandomPool)
+{
+ uno::Sequence < PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
+ OUString sTempName = rPath + "/";
+
+ if ( !GetMediaType().isEmpty() )
+ {
+ auto pPropSet = aPropSet.getArray();
+ pPropSet[PKG_MNFST_MEDIATYPE].Name = "MediaType";
+ pPropSet[PKG_MNFST_MEDIATYPE].Value <<= GetMediaType();
+ pPropSet[PKG_MNFST_VERSION].Name = "Version";
+ pPropSet[PKG_MNFST_VERSION].Value <<= GetVersion();
+ pPropSet[PKG_MNFST_FULLPATH].Name = "FullPath";
+ pPropSet[PKG_MNFST_FULLPATH].Value <<= sTempName;
+ }
+ else
+ aPropSet.realloc( 0 );
+
+ saveContents(sTempName, rManList, rZipOut, rEncryptionKey, oPBKDF2IterationCount, oArgon2Args, rRandomPool);
+
+ // folder can have a mediatype only in package format
+ if ( aPropSet.hasElements() && ( m_nFormat == embed::StorageFormats::PACKAGE ) )
+ rManList.push_back( aPropSet );
+
+ return true;
+}
+
+void ZipPackageFolder::saveContents(
+ const OUString &rPath,
+ std::vector < uno::Sequence < PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ ::std::optional<sal_Int32> const oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args,
+ const rtlRandomPool &rRandomPool ) const
+{
+ if ( maContents.empty() && !rPath.isEmpty() && m_nFormat != embed::StorageFormats::OFOPXML )
+ {
+ // it is an empty subfolder, use workaround to store it
+ ZipEntry* pTempEntry = new ZipEntry(aEntry);
+ pTempEntry->nPathLen = static_cast<sal_Int16>( OUStringToOString( rPath, RTL_TEXTENCODING_UTF8 ).getLength() );
+ pTempEntry->nExtraLen = -1;
+ pTempEntry->sPath = rPath;
+
+ try
+ {
+ ZipOutputStream::setEntry(pTempEntry);
+ rZipOut.writeLOC(pTempEntry);
+ rZipOut.rawCloseEntry();
+ }
+ catch ( ZipException& )
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ catch ( IOException& )
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+
+ bool bMimeTypeStreamStored = false;
+ OUString aMimeTypeStreamName("mimetype");
+ if ( m_nFormat == embed::StorageFormats::ZIP && rPath.isEmpty() )
+ {
+ // let the "mimetype" stream in root folder be stored as the first stream if it is zip format
+ ContentHash::const_iterator aIter = maContents.find ( aMimeTypeStreamName );
+ if ( aIter != maContents.end() && !(*aIter).second.bFolder )
+ {
+ bMimeTypeStreamStored = true;
+ if (!aIter->second.pStream->saveChild(rPath + aIter->first, rManList, rZipOut,
+ rEncryptionKey, oPBKDF2IterationCount, oArgon2Args, rRandomPool))
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+ }
+
+ for (const auto& [rShortName, rInfo] : maContents)
+ {
+ if ( !bMimeTypeStreamStored || rShortName != aMimeTypeStreamName )
+ {
+ if (rInfo.bFolder)
+ {
+ if (!rInfo.pFolder->saveChild(rPath + rShortName, rManList, rZipOut,
+ rEncryptionKey, oPBKDF2IterationCount, oArgon2Args, rRandomPool))
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+ else
+ {
+ if (!rInfo.pStream->saveChild(rPath + rShortName, rManList, rZipOut,
+ rEncryptionKey, oPBKDF2IterationCount, oArgon2Args, rRandomPool))
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+ }
+ }
+}
+
+void SAL_CALL ZipPackageFolder::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( aPropertyName == "MediaType" )
+ {
+ // TODO/LATER: activate when zip ucp is ready
+ // if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ // throw UnknownPropertyException(THROW_WHERE );
+
+ aValue >>= msMediaType;
+ }
+ else if ( aPropertyName == "Version" )
+ aValue >>= m_sVersion;
+ else if ( aPropertyName == "Size" )
+ aValue >>= aEntry.nSize;
+ else
+ throw UnknownPropertyException(aPropertyName);
+}
+uno::Any SAL_CALL ZipPackageFolder::getPropertyValue( const OUString& PropertyName )
+{
+ if ( PropertyName == "MediaType" )
+ {
+ // TODO/LATER: activate when zip ucp is ready
+ // if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ // throw UnknownPropertyException(THROW_WHERE );
+
+ return uno::Any ( msMediaType );
+ }
+ else if ( PropertyName == "Version" )
+ return uno::Any( m_sVersion );
+ else if ( PropertyName == "Size" )
+ return uno::Any ( aEntry.nSize );
+ else
+ throw UnknownPropertyException(PropertyName);
+}
+
+void ZipPackageFolder::doInsertByName ( ZipPackageEntry *pEntry, bool bSetParent )
+{
+ if ( pEntry->IsFolder() )
+ maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageFolder*>(pEntry)));
+ else
+ maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageStream*>(pEntry)));
+ if ( bSetParent )
+ pEntry->setParent ( *this );
+}
+
+OUString ZipPackageFolder::getImplementationName()
+{
+ return "ZipPackageFolder";
+}
+
+uno::Sequence< OUString > ZipPackageFolder::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.PackageFolder" };
+}
+
+sal_Bool SAL_CALL ZipPackageFolder::supportsService( OUString const & rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+
+ZipContentInfo::ZipContentInfo ( ZipPackageStream * pNewStream )
+: xPackageEntry ( pNewStream )
+, bFolder ( false )
+, pStream ( pNewStream )
+{
+}
+
+ZipContentInfo::ZipContentInfo ( ZipPackageFolder * pNewFolder )
+: xPackageEntry ( pNewFolder )
+, bFolder ( true )
+, pFolder ( pNewFolder )
+{
+}
+
+ZipContentInfo::ZipContentInfo( const ZipContentInfo& ) = default;
+ZipContentInfo::ZipContentInfo( ZipContentInfo&& ) = default;
+ZipContentInfo& ZipContentInfo::operator=( const ZipContentInfo& ) = default;
+ZipContentInfo& ZipContentInfo::operator=( ZipContentInfo&& ) = default;
+
+ZipContentInfo::~ZipContentInfo()
+{
+ if ( bFolder )
+ pFolder->clearParent();
+ else
+ pStream->clearParent();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageFolderEnumeration.cxx b/package/source/zippackage/ZipPackageFolderEnumeration.cxx
new file mode 100644
index 0000000000..080592c95f
--- /dev/null
+++ b/package/source/zippackage/ZipPackageFolderEnumeration.cxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ZipPackageFolderEnumeration.hxx"
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageFolderEnumeration::ZipPackageFolderEnumeration(ContentHash& rInput)
+ : rContents(rInput)
+ , aIterator(rContents.begin())
+{
+}
+
+ZipPackageFolderEnumeration::~ZipPackageFolderEnumeration() {}
+
+sal_Bool SAL_CALL ZipPackageFolderEnumeration::hasMoreElements()
+{
+ return (aIterator != rContents.end());
+}
+uno::Any SAL_CALL ZipPackageFolderEnumeration::nextElement()
+{
+ uno::Any aAny;
+ if (aIterator == rContents.end())
+ throw container::NoSuchElementException(THROW_WHERE);
+ aAny <<= uno::Reference(cppu::getXWeak((*aIterator).second.xPackageEntry.get()));
+ ++aIterator;
+ return aAny;
+}
+
+OUString ZipPackageFolderEnumeration::getImplementationName()
+{
+ return "ZipPackageFolderEnumeration";
+}
+
+uno::Sequence<OUString> ZipPackageFolderEnumeration::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aNames{ "com.sun.star.packages.PackageFolderEnumeration" };
+ return aNames;
+}
+
+sal_Bool SAL_CALL ZipPackageFolderEnumeration::supportsService(OUString const& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageFolderEnumeration.hxx b/package/source/zippackage/ZipPackageFolderEnumeration.hxx
new file mode 100644
index 0000000000..f3b805b2aa
--- /dev/null
+++ b/package/source/zippackage/ZipPackageFolderEnumeration.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPPACKAGE_ZIPPACKAGEFOLDERENUMERATION_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPPACKAGE_ZIPPACKAGEFOLDERENUMERATION_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <ZipPackageFolder.hxx>
+
+class ZipPackageFolderEnumeration final
+ : public cppu::WeakImplHelper<css::container::XEnumeration, css::lang::XServiceInfo>
+{
+ ContentHash& rContents;
+ ContentHash::const_iterator aIterator;
+
+public:
+ //ZipPackageFolderEnumeration (unordered_map < OUString, css::uno::Reference < css::container::XNamed >, hashFunc, eqFunc > &rInput);
+ ZipPackageFolderEnumeration(ContentHash& rInput);
+ virtual ~ZipPackageFolderEnumeration() override;
+
+ // XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+ virtual css::uno::Any SAL_CALL nextElement() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageSink.cxx b/package/source/zippackage/ZipPackageSink.cxx
new file mode 100644
index 0000000000..103af9d6ed
--- /dev/null
+++ b/package/source/zippackage/ZipPackageSink.cxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ZipPackageSink.hxx"
+
+ZipPackageSink::ZipPackageSink()
+ : xStream(css::uno::Reference<css::io::XInputStream>(nullptr))
+{
+}
+ZipPackageSink::~ZipPackageSink() {}
+void SAL_CALL
+ZipPackageSink::setInputStream(const css::uno::Reference<css::io::XInputStream>& aStream)
+{
+ xStream = aStream;
+}
+css::uno::Reference<css::io::XInputStream> SAL_CALL ZipPackageSink::getInputStream()
+{
+ return xStream;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageSink.hxx b/package/source/zippackage/ZipPackageSink.hxx
new file mode 100644
index 0000000000..9bc406c89e
--- /dev/null
+++ b/package/source/zippackage/ZipPackageSink.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPPACKAGE_ZIPPACKAGESINK_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPPACKAGE_ZIPPACKAGESINK_HXX
+
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <cppuhelper/implbase.hxx>
+
+class ZipPackageSink final : public ::cppu::WeakImplHelper<css::io::XActiveDataSink>
+{
+ css::uno::Reference<css::io::XInputStream> xStream;
+
+public:
+ ZipPackageSink();
+ virtual ~ZipPackageSink() override;
+ virtual void SAL_CALL
+ setInputStream(const css::uno::Reference<css::io::XInputStream>& aStream) override;
+ virtual css::uno::Reference<css::io::XInputStream> SAL_CALL getInputStream() override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageStream.cxx b/package/source/zippackage/ZipPackageStream.cxx
new file mode 100644
index 0000000000..d3068a6665
--- /dev/null
+++ b/package/source/zippackage/ZipPackageStream.cxx
@@ -0,0 +1,1359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipPackageStream.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/packages/NoRawFormatException.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/packages/NoEncryptionException.hpp>
+#include <com/sun/star/packages/zip/ZipException.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/KDFID.hpp>
+
+#include <CRC32.hxx>
+#include <ZipOutputEntry.hxx>
+#include <ZipOutputStream.hxx>
+#include <ZipPackage.hxx>
+#include <ZipFile.hxx>
+#include <EncryptedDataHeader.hxx>
+#include <osl/diagnose.h>
+#include "wrapstreamforshare.hxx"
+
+#include <comphelper/seekableinput.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <rtl/random.h>
+#include <sal/log.hxx>
+#include <o3tl/unreachable.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <PackageConstants.hxx>
+
+#include <algorithm>
+#include <cstddef>
+
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star;
+using namespace cppu;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageStream::ZipPackageStream ( ZipPackage & rNewPackage,
+ const uno::Reference< XComponentContext >& xContext,
+ sal_Int32 nFormat,
+ bool bAllowRemoveOnInsert )
+: m_rZipPackage( rNewPackage )
+, m_bToBeCompressed ( true )
+, m_bToBeEncrypted ( false )
+, m_bHaveOwnKey ( false )
+, m_bIsEncrypted ( false )
+, m_nImportedStartKeyAlgorithm( 0 )
+, m_nImportedEncryptionAlgorithm( 0 )
+, m_nImportedDerivedKeySize( 0 )
+, m_nStreamMode( PACKAGE_STREAM_NOTSET )
+, m_nMagicalHackPos( 0 )
+, m_nMagicalHackSize( 0 )
+, m_nOwnStreamOrigSize( 0 )
+, m_bHasSeekable( false )
+, m_bCompressedIsSetFromOutside( false )
+, m_bFromManifest( false )
+, m_bUseWinEncoding( false )
+, m_bRawStream( false )
+{
+ m_xContext = xContext;
+ m_nFormat = nFormat;
+ mbAllowRemoveOnInsert = bAllowRemoveOnInsert;
+ SetFolder ( false );
+ aEntry.nVersion = -1;
+ aEntry.nFlag = 0;
+ aEntry.nMethod = -1;
+ aEntry.nTime = -1;
+ aEntry.nCrc = -1;
+ aEntry.nCompressedSize = -1;
+ aEntry.nSize = -1;
+ aEntry.nOffset = -1;
+ aEntry.nPathLen = -1;
+ aEntry.nExtraLen = -1;
+}
+
+ZipPackageStream::~ZipPackageStream()
+{
+}
+
+void ZipPackageStream::setZipEntryOnLoading( const ZipEntry &rInEntry )
+{
+ aEntry.nVersion = rInEntry.nVersion;
+ aEntry.nFlag = rInEntry.nFlag;
+ aEntry.nMethod = rInEntry.nMethod;
+ aEntry.nTime = rInEntry.nTime;
+ aEntry.nCrc = rInEntry.nCrc;
+ aEntry.nCompressedSize = rInEntry.nCompressedSize;
+ aEntry.nSize = rInEntry.nSize;
+ aEntry.nOffset = rInEntry.nOffset;
+ aEntry.sPath = rInEntry.sPath;
+ aEntry.nPathLen = rInEntry.nPathLen;
+ aEntry.nExtraLen = rInEntry.nExtraLen;
+
+ if ( aEntry.nMethod == STORED )
+ m_bToBeCompressed = false;
+}
+
+uno::Reference< io::XInputStream > const & ZipPackageStream::GetOwnSeekStream()
+{
+ if ( !m_bHasSeekable && m_xStream.is() )
+ {
+ // The package component requires that every stream either be FROM a package or it must support XSeekable!
+ // The only exception is a nonseekable stream that is provided only for storing, if such a stream
+ // is accessed before commit it MUST be wrapped.
+ // Wrap the stream in case it is not seekable
+ m_xStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xStream, m_xContext );
+ uno::Reference< io::XSeekable > xSeek( m_xStream, UNO_QUERY_THROW );
+
+ m_bHasSeekable = true;
+ }
+
+ return m_xStream;
+}
+
+uno::Reference< io::XInputStream > ZipPackageStream::GetRawEncrStreamNoHeaderCopy()
+{
+ if ( m_nStreamMode != PACKAGE_STREAM_RAW || !GetOwnSeekStream().is() )
+ throw io::IOException(THROW_WHERE );
+
+ if ( m_xBaseEncryptionData.is() )
+ throw ZipIOException(THROW_WHERE "Encrypted stream without encryption data!" );
+
+ uno::Reference< io::XSeekable > xSeek( GetOwnSeekStream(), UNO_QUERY );
+ if ( !xSeek.is() )
+ throw ZipIOException(THROW_WHERE "The stream must be seekable!" );
+
+ // skip header
+ xSeek->seek( n_ConstHeaderSize + m_xBaseEncryptionData->m_aInitVector.getLength() +
+ m_xBaseEncryptionData->m_aSalt.getLength() + m_xBaseEncryptionData->m_aDigest.getLength() );
+
+ // create temporary stream
+ rtl::Reference < utl::TempFileFastService > xTempFile = new utl::TempFileFastService;
+ uno::Reference < io::XInputStream > xTempIn = xTempFile->getInputStream();
+
+ // copy the raw stream to the temporary file starting from the current position
+ ::comphelper::OStorageHelper::CopyInputToOutput( GetOwnSeekStream(), xTempFile );
+ xTempFile->closeOutput();
+ xTempFile->seek( 0 );
+
+ return xTempIn;
+}
+
+sal_Int32 ZipPackageStream::GetEncryptionAlgorithm() const
+{
+ return m_nImportedEncryptionAlgorithm ? m_nImportedEncryptionAlgorithm : m_rZipPackage.GetEncAlgID();
+}
+
+sal_Int32 ZipPackageStream::GetIVSize() const
+{
+ switch (GetEncryptionAlgorithm())
+ {
+ case css::xml::crypto::CipherID::BLOWFISH_CFB_8:
+ return 8;
+ case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
+ return 16;
+ case css::xml::crypto::CipherID::AES_GCM_W3C:
+ return 12;
+ default:
+ O3TL_UNREACHABLE;
+ }
+}
+
+::rtl::Reference<EncryptionData> ZipPackageStream::GetEncryptionData(Bugs const bugs)
+{
+ ::rtl::Reference< EncryptionData > xResult;
+ if ( m_xBaseEncryptionData.is() )
+ xResult = new EncryptionData(
+ *m_xBaseEncryptionData,
+ GetEncryptionKey(bugs),
+ GetEncryptionAlgorithm(),
+ m_oImportedChecksumAlgorithm ? m_oImportedChecksumAlgorithm : m_rZipPackage.GetChecksumAlgID(),
+ m_nImportedDerivedKeySize ? m_nImportedDerivedKeySize : m_rZipPackage.GetDefaultDerivedKeySize(),
+ GetStartKeyGenID(),
+ bugs != Bugs::None);
+
+ return xResult;
+}
+
+uno::Sequence<sal_Int8> ZipPackageStream::GetEncryptionKey(Bugs const bugs)
+{
+ uno::Sequence< sal_Int8 > aResult;
+ sal_Int32 nKeyGenID = GetStartKeyGenID();
+ bool const bUseWinEncoding = (bugs == Bugs::WinEncodingWrongSHA1 || m_bUseWinEncoding);
+
+ if ( m_bHaveOwnKey && m_aStorageEncryptionKeys.hasElements() )
+ {
+ OUString aNameToFind;
+ if ( nKeyGenID == xml::crypto::DigestID::SHA256 )
+ aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
+ else if ( nKeyGenID == xml::crypto::DigestID::SHA1 )
+ {
+ aNameToFind = bUseWinEncoding
+ ? PACKAGE_ENCRYPTIONDATA_SHA1MS1252
+ : (bugs == Bugs::WrongSHA1)
+ ? PACKAGE_ENCRYPTIONDATA_SHA1UTF8
+ : PACKAGE_ENCRYPTIONDATA_SHA1CORRECT;
+ }
+ else
+ throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
+
+ for ( const auto& rKey : std::as_const(m_aStorageEncryptionKeys) )
+ if ( rKey.Name == aNameToFind )
+ rKey.Value >>= aResult;
+
+ // empty keys are not allowed here
+ // so it is not important whether there is no key, or the key is empty, it is an error
+ if ( !aResult.hasElements() )
+ throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
+ }
+ else
+ aResult = m_aEncryptionKey;
+
+ if ( !aResult.hasElements() || !m_bHaveOwnKey )
+ aResult = m_rZipPackage.GetEncryptionKey();
+
+ return aResult;
+}
+
+sal_Int32 ZipPackageStream::GetStartKeyGenID() const
+{
+ // generally should all the streams use the same Start Key
+ // but if raw copy without password takes place, we should preserve the imported algorithm
+ return m_nImportedStartKeyAlgorithm ? m_nImportedStartKeyAlgorithm : m_rZipPackage.GetStartKeyGenID();
+}
+
+uno::Reference< io::XInputStream > ZipPackageStream::TryToGetRawFromDataStream( bool bAddHeaderForEncr )
+{
+ if ( m_nStreamMode != PACKAGE_STREAM_DATA || !GetOwnSeekStream().is() || ( bAddHeaderForEncr && !m_bToBeEncrypted ) )
+ throw packages::NoEncryptionException(THROW_WHERE );
+
+ Sequence< sal_Int8 > aKey;
+
+ if ( m_bToBeEncrypted )
+ {
+ aKey = GetEncryptionKey();
+ if ( !aKey.hasElements() )
+ throw packages::NoEncryptionException(THROW_WHERE );
+ }
+
+ try
+ {
+ // create temporary file
+ uno::Reference < io::XStream > xTempStream(new utl::TempFileFastService);
+
+ // create a package based on it
+ rtl::Reference<ZipPackage> pPackage = new ZipPackage( m_xContext );
+
+ Sequence< Any > aArgs{ Any(xTempStream) };
+ pPackage->initialize( aArgs );
+
+ // create a new package stream
+ uno::Reference< XDataSinkEncrSupport > xNewPackStream( pPackage->createInstance(), UNO_QUERY_THROW );
+ xNewPackStream->setDataStream(
+ new WrapStreamForShare(GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef()));
+
+ uno::Reference< XPropertySet > xNewPSProps( xNewPackStream, UNO_QUERY_THROW );
+
+ // copy all the properties of this stream to the new stream
+ xNewPSProps->setPropertyValue("MediaType", Any( msMediaType ) );
+ xNewPSProps->setPropertyValue("Compressed", Any( m_bToBeCompressed ) );
+ if ( m_bToBeEncrypted )
+ {
+ xNewPSProps->setPropertyValue(ENCRYPTION_KEY_PROPERTY, Any( aKey ) );
+ xNewPSProps->setPropertyValue("Encrypted", Any( true ) );
+ }
+
+ // insert a new stream in the package
+ uno::Reference< XInterface > xTmp;
+ Any aRoot = pPackage->getByHierarchicalName("/");
+ aRoot >>= xTmp;
+ uno::Reference< container::XNameContainer > xRootNameContainer( xTmp, UNO_QUERY_THROW );
+
+ uno::Reference< XInterface > xNPSDummy( xNewPackStream, UNO_QUERY );
+ xRootNameContainer->insertByName("dummy", Any( xNPSDummy ) );
+
+ // commit the temporary package
+ pPackage->commitChanges();
+
+ // get raw stream from the temporary package
+ uno::Reference< io::XInputStream > xInRaw;
+ if ( bAddHeaderForEncr )
+ xInRaw = xNewPackStream->getRawStream();
+ else
+ xInRaw = xNewPackStream->getPlainRawStream();
+
+ // create another temporary file
+ rtl::Reference < utl::TempFileFastService > xTempOut = new utl::TempFileFastService;
+ uno::Reference < io::XInputStream > xTempIn( xTempOut );
+
+ // copy the raw stream to the temporary file
+ ::comphelper::OStorageHelper::CopyInputToOutput( xInRaw, xTempOut );
+ xTempOut->closeOutput();
+ xTempOut->seek( 0 );
+
+ // close raw stream, package stream and folder
+ xInRaw.clear();
+ xNewPSProps.clear();
+ xNPSDummy.clear();
+ xNewPackStream.clear();
+ xTmp.clear();
+ xRootNameContainer.clear();
+
+ // return the stream representing the first temporary file
+ return xTempIn;
+ }
+ catch ( RuntimeException& )
+ {
+ throw;
+ }
+ catch ( Exception& )
+ {
+ }
+
+ throw io::IOException(THROW_WHERE );
+}
+
+// presumably the purpose of this is to transfer encrypted streams between
+// storages, needed for password-protected macros in documents, which is
+// tragically a feature that exists
+bool ZipPackageStream::ParsePackageRawStream()
+{
+ OSL_ENSURE( GetOwnSeekStream().is(), "A stream must be provided!" );
+
+ if ( !GetOwnSeekStream().is() )
+ return false;
+
+ bool bOk = false;
+
+ ::rtl::Reference< BaseEncryptionData > xTempEncrData;
+ Sequence < sal_Int8 > aHeader ( 4 );
+
+ try
+ {
+ if ( GetOwnSeekStream()->readBytes ( aHeader, 4 ) == 4 )
+ {
+ const sal_Int8 *pHeader = aHeader.getConstArray();
+ sal_uInt32 nHeader = ( pHeader [0] & 0xFF ) |
+ ( pHeader [1] & 0xFF ) << 8 |
+ ( pHeader [2] & 0xFF ) << 16 |
+ ( pHeader [3] & 0xFF ) << 24;
+ if ( nHeader == n_ConstHeader )
+ {
+ // this is one of our god-awful, but extremely devious hacks, everyone cheer
+ xTempEncrData = new BaseEncryptionData;
+
+ OUString aMediaType;
+ sal_Int32 nEncAlgorithm = 0;
+ sal_Int32 nChecksumAlgorithm = 0;
+ sal_Int32 nDerivedKeySize = 0;
+ sal_Int32 nStartKeyGenID = 0;
+ sal_Int32 nMagHackSize = 0;
+ if ( ZipFile::StaticFillData( xTempEncrData, nEncAlgorithm, nChecksumAlgorithm, nDerivedKeySize, nStartKeyGenID, nMagHackSize, aMediaType, GetOwnSeekStream() ) )
+ {
+ // We'll want to skip the data we've just read, so calculate how much we just read
+ // and remember it
+ m_nMagicalHackPos = n_ConstHeaderSize + xTempEncrData->m_aSalt.getLength()
+ + xTempEncrData->m_aInitVector.getLength()
+ + xTempEncrData->m_aDigest.getLength()
+ + aMediaType.getLength() * sizeof( sal_Unicode );
+ m_nImportedEncryptionAlgorithm = nEncAlgorithm;
+ if (nChecksumAlgorithm == 0)
+ {
+ m_oImportedChecksumAlgorithm.reset();
+ }
+ else
+ {
+ m_oImportedChecksumAlgorithm.emplace(nChecksumAlgorithm);
+ }
+ m_nImportedDerivedKeySize = nDerivedKeySize;
+ m_nImportedStartKeyAlgorithm = nStartKeyGenID;
+ m_nMagicalHackSize = nMagHackSize;
+ msMediaType = aMediaType;
+
+ bOk = true;
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ if ( !bOk )
+ {
+ // the provided stream is not a raw stream
+ return false;
+ }
+
+ m_xBaseEncryptionData = xTempEncrData;
+ SetIsEncrypted ( true );
+ // it's already compressed and encrypted
+ m_bToBeEncrypted = m_bToBeCompressed = false;
+
+ return true;
+}
+
+static void ImplSetStoredData( ZipEntry & rEntry, uno::Reference< io::XInputStream> const & rStream )
+{
+ // It's very annoying that we have to do this, but lots of zip packages
+ // don't allow data descriptors for STORED streams, meaning we have to
+ // know the size and CRC32 of uncompressed streams before we actually
+ // write them !
+ CRC32 aCRC32;
+ rEntry.nMethod = STORED;
+ rEntry.nCompressedSize = rEntry.nSize = aCRC32.updateStream ( rStream );
+ rEntry.nCrc = aCRC32.getValue();
+}
+
+bool ZipPackageStream::saveChild(
+ const OUString &rPath,
+ std::vector < uno::Sequence < beans::PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ ::std::optional<sal_Int32> const oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args,
+ const rtlRandomPool &rRandomPool)
+{
+ bool bSuccess = true;
+
+ static constexpr OUString sDigestProperty (u"Digest"_ustr);
+ static constexpr OUString sEncryptionAlgProperty (u"EncryptionAlgorithm"_ustr);
+ static constexpr OUString sStartKeyAlgProperty (u"StartKeyAlgorithm"_ustr);
+ static constexpr OUString sDigestAlgProperty (u"DigestAlgorithm"_ustr);
+ static constexpr OUString sDerivedKeySizeProperty (u"DerivedKeySize"_ustr);
+
+ uno::Sequence < beans::PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
+
+ // In case the entry we are reading is also the entry we are writing, we will
+ // store the ZipEntry data in pTempEntry
+
+ // if pTempEntry is necessary, it will be released and passed to the ZipOutputStream
+ // and be deleted in the ZipOutputStream destructor
+ std::unique_ptr < ZipEntry > pAutoTempEntry ( new ZipEntry(aEntry) );
+ ZipEntry* pTempEntry = pAutoTempEntry.get();
+
+ pTempEntry->sPath = rPath;
+ pTempEntry->nPathLen = static_cast<sal_Int16>( OUStringToOString( pTempEntry->sPath, RTL_TEXTENCODING_UTF8 ).getLength() );
+
+ const bool bToBeEncrypted = m_bToBeEncrypted && (rEncryptionKey.hasElements() || m_bHaveOwnKey);
+ const bool bToBeCompressed = bToBeEncrypted || m_bToBeCompressed;
+
+ auto pPropSet = aPropSet.getArray();
+ pPropSet[PKG_MNFST_MEDIATYPE].Name = "MediaType";
+ pPropSet[PKG_MNFST_MEDIATYPE].Value <<= GetMediaType( );
+ pPropSet[PKG_MNFST_VERSION].Name = "Version";
+ pPropSet[PKG_MNFST_VERSION].Value <<= OUString(); // no version is stored for streams currently
+ pPropSet[PKG_MNFST_FULLPATH].Name = "FullPath";
+ pPropSet[PKG_MNFST_FULLPATH].Value <<= pTempEntry->sPath;
+
+ OSL_ENSURE( m_nStreamMode != PACKAGE_STREAM_NOTSET, "Unacceptable ZipPackageStream mode!" );
+
+ m_bRawStream = false;
+ if ( m_nStreamMode == PACKAGE_STREAM_DETECT )
+ m_bRawStream = ParsePackageRawStream();
+ else if ( m_nStreamMode == PACKAGE_STREAM_RAW )
+ m_bRawStream = true;
+
+ bool bBackgroundThreadDeflate = false;
+ bool bTransportOwnEncrStreamAsRaw = false;
+ // During the storing the original size of the stream can be changed
+ // TODO/LATER: get rid of this hack
+ m_nOwnStreamOrigSize = m_bRawStream ? m_nMagicalHackSize : aEntry.nSize;
+
+ bool bUseNonSeekableAccess = false;
+ uno::Reference < io::XInputStream > xStream;
+ if ( !IsPackageMember() && !m_bRawStream && !bToBeEncrypted && bToBeCompressed )
+ {
+ // the stream is not a package member, not a raw stream,
+ // it should not be encrypted and it should be compressed,
+ // in this case nonseekable access can be used
+
+ xStream = m_xStream;
+ uno::Reference < io::XSeekable > xSeek ( xStream, uno::UNO_QUERY );
+
+ bUseNonSeekableAccess = ( xStream.is() && !xSeek.is() );
+ }
+
+ if ( !bUseNonSeekableAccess )
+ {
+ xStream = getRawData();
+
+ if ( !xStream.is() )
+ {
+ OSL_FAIL( "ZipPackageStream didn't have a stream associated with it, skipping!" );
+ bSuccess = false;
+ return bSuccess;
+ }
+
+ uno::Reference < io::XSeekable > xSeek ( xStream, uno::UNO_QUERY );
+ try
+ {
+ if ( xSeek.is() )
+ {
+ // If the stream is a raw one, then we should be positioned
+ // at the beginning of the actual data
+ if ( !bToBeCompressed || m_bRawStream )
+ {
+ // The raw stream can neither be encrypted nor connected
+ OSL_ENSURE( !m_bRawStream || !(bToBeCompressed || bToBeEncrypted), "The stream is already encrypted!" );
+ xSeek->seek ( m_bRawStream ? m_nMagicalHackPos : 0 );
+ ImplSetStoredData ( *pTempEntry, xStream );
+
+ // TODO/LATER: Get rid of hacks related to switching of Flag Method and Size properties!
+ }
+ else if ( bToBeEncrypted )
+ {
+ // this is the correct original size
+ pTempEntry->nSize = xSeek->getLength();
+ m_nOwnStreamOrigSize = pTempEntry->nSize;
+ }
+
+ xSeek->seek ( 0 );
+ }
+ else
+ {
+ // Okay, we don't have an xSeekable stream. This is possibly bad.
+ // check if it's one of our own streams, if it is then we know that
+ // each time we ask for it we'll get a new stream that will be
+ // at position zero...otherwise, assert and skip this stream...
+ if ( IsPackageMember() )
+ {
+ // if the password has been changed then the stream should not be package member any more
+ if ( m_bIsEncrypted && m_bToBeEncrypted )
+ {
+ // Should be handled close to the raw stream handling
+ bTransportOwnEncrStreamAsRaw = true;
+ pTempEntry->nMethod = STORED;
+
+ // TODO/LATER: get rid of this situation
+ // this size should be different from the one that will be stored in manifest.xml
+ // it is used in storing algorithms and after storing the correct size will be set
+ pTempEntry->nSize = pTempEntry->nCompressedSize;
+ }
+ }
+ else
+ {
+ bSuccess = false;
+ return bSuccess;
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ bSuccess = false;
+ return bSuccess;
+ }
+
+ if ( bToBeEncrypted || m_bRawStream || bTransportOwnEncrStreamAsRaw )
+ {
+ if ( bToBeEncrypted && !bTransportOwnEncrStreamAsRaw )
+ {
+ uno::Sequence<sal_Int8> aSalt(16);
+ // note: for GCM it's particularly important that IV is unique
+ uno::Sequence<sal_Int8> aVector(GetIVSize());
+ rtl_random_getBytes ( rRandomPool, aSalt.getArray(), 16 );
+ rtl_random_getBytes ( rRandomPool, aVector.getArray(), aVector.getLength() );
+ if ( !m_bHaveOwnKey )
+ {
+ m_aEncryptionKey = rEncryptionKey;
+ m_aStorageEncryptionKeys.realloc( 0 );
+ }
+
+ setInitialisationVector ( aVector );
+ setSalt ( aSalt );
+ setIterationCount(oPBKDF2IterationCount);
+ setArgon2Args(oArgon2Args);
+ }
+
+ // last property is digest, which is inserted later if we didn't have
+ // a magic header
+ aPropSet.realloc(PKG_SIZE_ENCR_MNFST);
+ pPropSet = aPropSet.getArray();
+ pPropSet[PKG_MNFST_INIVECTOR].Name = "InitialisationVector";
+ pPropSet[PKG_MNFST_INIVECTOR].Value <<= m_xBaseEncryptionData->m_aInitVector;
+ pPropSet[PKG_MNFST_SALT].Name = "Salt";
+ pPropSet[PKG_MNFST_SALT].Value <<= m_xBaseEncryptionData->m_aSalt;
+ if (m_xBaseEncryptionData->m_oArgon2Args)
+ {
+ pPropSet[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ pPropSet[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::Argon2id;
+ pPropSet[PKG_MNFST_ARGON2ARGS].Name = "Argon2Args";
+ uno::Sequence<sal_Int32> const args{
+ ::std::get<0>(*m_xBaseEncryptionData->m_oArgon2Args),
+ ::std::get<1>(*m_xBaseEncryptionData->m_oArgon2Args),
+ ::std::get<2>(*m_xBaseEncryptionData->m_oArgon2Args) };
+ pPropSet[PKG_MNFST_ARGON2ARGS].Value <<= args;
+ }
+ else if (m_xBaseEncryptionData->m_oPBKDFIterationCount)
+ {
+ pPropSet[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ pPropSet[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PBKDF2;
+ pPropSet[PKG_MNFST_ITERATION].Name = "IterationCount";
+ pPropSet[PKG_MNFST_ITERATION].Value <<= *m_xBaseEncryptionData->m_oPBKDFIterationCount;
+ }
+ else
+ {
+ pPropSet[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ pPropSet[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
+ }
+
+ // Need to store the uncompressed size in the manifest
+ OSL_ENSURE( m_nOwnStreamOrigSize >= 0, "The stream size was not correctly initialized!" );
+ pPropSet[PKG_MNFST_UCOMPSIZE].Name = "Size";
+ pPropSet[PKG_MNFST_UCOMPSIZE].Value <<= m_nOwnStreamOrigSize;
+
+ if ( m_bRawStream || bTransportOwnEncrStreamAsRaw )
+ {
+ ::rtl::Reference< EncryptionData > xEncData = GetEncryptionData();
+ if ( !xEncData.is() )
+ throw uno::RuntimeException();
+
+ pPropSet[PKG_MNFST_ENCALG].Name = sEncryptionAlgProperty;
+ pPropSet[PKG_MNFST_ENCALG].Value <<= xEncData->m_nEncAlg;
+ pPropSet[PKG_MNFST_STARTALG].Name = sStartKeyAlgProperty;
+ pPropSet[PKG_MNFST_STARTALG].Value <<= xEncData->m_nStartKeyGenID;
+ if (xEncData->m_oCheckAlg)
+ {
+ assert(xEncData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C);
+ pPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
+ pPropSet[PKG_MNFST_DIGEST].Value <<= m_xBaseEncryptionData->m_aDigest;
+ pPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
+ pPropSet[PKG_MNFST_DIGESTALG].Value <<= *xEncData->m_oCheckAlg;
+ }
+ pPropSet[PKG_MNFST_DERKEYSIZE].Name = sDerivedKeySizeProperty;
+ pPropSet[PKG_MNFST_DERKEYSIZE].Value <<= xEncData->m_nDerivedKeySize;
+ }
+ }
+ }
+
+ // If the entry is already stored in the zip file in the format we
+ // want for this write...copy it raw
+ if ( !bUseNonSeekableAccess
+ && ( m_bRawStream || bTransportOwnEncrStreamAsRaw
+ || ( IsPackageMember() && !bToBeEncrypted
+ && ( ( aEntry.nMethod == DEFLATED && bToBeCompressed )
+ || ( aEntry.nMethod == STORED && !bToBeCompressed ) ) ) ) )
+ {
+ // If it's a PackageMember, then it's an unbuffered stream and we need
+ // to get a new version of it as we can't seek backwards.
+ if ( IsPackageMember() )
+ {
+ xStream = getRawData();
+ if ( !xStream.is() )
+ {
+ // Make sure that we actually _got_ a new one !
+ bSuccess = false;
+ return bSuccess;
+ }
+ }
+
+ try
+ {
+ if ( m_bRawStream )
+ xStream->skipBytes( m_nMagicalHackPos );
+
+ ZipOutputStream::setEntry(pTempEntry);
+ rZipOut.writeLOC(pTempEntry);
+ // coverity[leaked_storage] - the entry is provided to the ZipOutputStream that will delete it
+ pAutoTempEntry.release();
+
+ uno::Sequence < sal_Int8 > aSeq ( n_ConstBufferSize );
+ sal_Int32 nLength;
+
+ do
+ {
+ nLength = xStream->readBytes( aSeq, n_ConstBufferSize );
+ if (nLength != n_ConstBufferSize)
+ aSeq.realloc(nLength);
+
+ rZipOut.rawWrite(aSeq);
+ }
+ while ( nLength == n_ConstBufferSize );
+
+ rZipOut.rawCloseEntry();
+ }
+ catch ( ZipException& )
+ {
+ bSuccess = false;
+ }
+ catch ( io::IOException& )
+ {
+ bSuccess = false;
+ }
+ }
+ else
+ {
+ // This stream is definitely not a raw stream
+
+ // If nonseekable access is used the stream should be at the beginning and
+ // is useless after the storing. Thus if the storing fails the package should
+ // be thrown away ( as actually it is done currently )!
+ // To allow to reuse the package after the error, the optimization must be removed!
+
+ // If it's a PackageMember, then our previous reference held a 'raw' stream
+ // so we need to re-get it, unencrypted, uncompressed and positioned at the
+ // beginning of the stream
+ if ( IsPackageMember() )
+ {
+ xStream = getInputStream();
+ if ( !xStream.is() )
+ {
+ // Make sure that we actually _got_ a new one !
+ bSuccess = false;
+ return bSuccess;
+ }
+ }
+
+ if ( bToBeCompressed )
+ {
+ pTempEntry->nMethod = DEFLATED;
+ pTempEntry->nCrc = -1;
+ pTempEntry->nCompressedSize = pTempEntry->nSize = -1;
+ }
+
+ uno::Reference< io::XSeekable > xSeek(xStream, uno::UNO_QUERY);
+ // It's not worth to deflate jpegs to save ~1% in a slow process
+ // Unfortunately, does not work for streams protected by password
+ if (xSeek.is() && msMediaType.endsWith("/jpeg") && !m_bToBeEncrypted && !m_bToBeCompressed)
+ {
+ ImplSetStoredData(*pTempEntry, xStream);
+ xSeek->seek(0);
+ }
+
+ try
+ {
+ ZipOutputStream::setEntry(pTempEntry);
+ // the entry is provided to the ZipOutputStream that will delete it
+ pAutoTempEntry.release();
+
+ if (pTempEntry->nMethod == STORED)
+ {
+ sal_Int32 nLength;
+ uno::Sequence< sal_Int8 > aSeq(n_ConstBufferSize);
+ rZipOut.writeLOC(pTempEntry, bToBeEncrypted);
+ do
+ {
+ nLength = xStream->readBytes(aSeq, n_ConstBufferSize);
+ if (nLength != n_ConstBufferSize)
+ aSeq.realloc(nLength);
+
+ rZipOut.rawWrite(aSeq);
+ }
+ while ( nLength == n_ConstBufferSize );
+ rZipOut.rawCloseEntry(bToBeEncrypted);
+ }
+ else
+ {
+ // tdf#89236 Encrypting in a background thread does not work
+ bBackgroundThreadDeflate = !bToBeEncrypted;
+ // Do not deflate small streams using threads. XSeekable's getLength()
+ // gives the full size, XInputStream's available() may not be
+ // the full size, but it appears that at this point it usually is.
+ sal_Int64 estimatedSize = xSeek.is() ? xSeek->getLength() : xStream->available();
+
+ if (estimatedSize > 1000000)
+ {
+ // Use ThreadDeflater which will split the stream into blocks and compress
+ // them in threads, but not in background (i.e. writeStream() will block).
+ // This is suitable for large data.
+ bBackgroundThreadDeflate = false;
+ rZipOut.writeLOC(pTempEntry, bToBeEncrypted);
+ ZipOutputEntryParallel aZipEntry(rZipOut.getStream(), m_xContext, *pTempEntry, this, bToBeEncrypted);
+ aZipEntry.writeStream(xStream);
+ rZipOut.rawCloseEntry(bToBeEncrypted);
+ }
+ else if (bBackgroundThreadDeflate && estimatedSize > 100000)
+ {
+ // tdf#93553 limit to a useful amount of pending tasks. Having way too many
+ // tasks pending may use a lot of memory. Take number of available
+ // cores and allow 4-times the amount for having the queue well filled. The
+ // 2nd parameter is the time to wait between cleanups in 10th of a second.
+ // Both values may be added to the configuration settings if needed.
+ static std::size_t nAllowedTasks(comphelper::ThreadPool::getPreferredConcurrency() * 4); //TODO: overflow
+ rZipOut.reduceScheduledThreadTasksToGivenNumberOrLess(nAllowedTasks);
+
+ // Start a new thread task deflating this zip entry
+ ZipOutputEntryInThread *pZipEntry = new ZipOutputEntryInThread(
+ m_xContext, *pTempEntry, this, bToBeEncrypted);
+ rZipOut.addDeflatingThreadTask( pZipEntry,
+ pZipEntry->createTask( rZipOut.getThreadTaskTag(), xStream) );
+ }
+ else
+ {
+ bBackgroundThreadDeflate = false;
+ rZipOut.writeLOC(pTempEntry, bToBeEncrypted);
+ ZipOutputEntry aZipEntry(rZipOut.getStream(), m_xContext, *pTempEntry, this, bToBeEncrypted);
+ aZipEntry.writeStream(xStream);
+ rZipOut.rawCloseEntry(bToBeEncrypted);
+ }
+ }
+ }
+ catch ( ZipException& )
+ {
+ bSuccess = false;
+ }
+ catch ( io::IOException& )
+ {
+ bSuccess = false;
+ }
+
+ if ( bToBeEncrypted )
+ {
+ ::rtl::Reference< EncryptionData > xEncData = GetEncryptionData();
+ if ( !xEncData.is() )
+ throw uno::RuntimeException();
+
+ // very confusing: half the encryption properties are
+ // unconditionally added above and the other half conditionally;
+ // assert that we have the expected group and not duplicates
+ assert(std::any_of(aPropSet.begin(), aPropSet.end(), [](auto const& it){ return it.Name == "Salt"; }));
+ assert(!std::any_of(aPropSet.begin(), aPropSet.end(), [](auto const& it){ return it.Name == sEncryptionAlgProperty; }));
+
+ pPropSet[PKG_MNFST_ENCALG].Name = sEncryptionAlgProperty;
+ pPropSet[PKG_MNFST_ENCALG].Value <<= xEncData->m_nEncAlg;
+ pPropSet[PKG_MNFST_STARTALG].Name = sStartKeyAlgProperty;
+ pPropSet[PKG_MNFST_STARTALG].Value <<= xEncData->m_nStartKeyGenID;
+ if (xEncData->m_oCheckAlg)
+ {
+ assert(xEncData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C);
+ pPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
+ pPropSet[PKG_MNFST_DIGEST].Value <<= m_xBaseEncryptionData->m_aDigest;
+ pPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
+ pPropSet[PKG_MNFST_DIGESTALG].Value <<= *xEncData->m_oCheckAlg;
+ }
+ pPropSet[PKG_MNFST_DERKEYSIZE].Name = sDerivedKeySizeProperty;
+ pPropSet[PKG_MNFST_DERKEYSIZE].Value <<= xEncData->m_nDerivedKeySize;
+
+ SetIsEncrypted ( true );
+ }
+ }
+
+ if (bSuccess && !bBackgroundThreadDeflate)
+ successfullyWritten(pTempEntry);
+
+ if ( aPropSet.hasElements()
+ && ( m_nFormat == embed::StorageFormats::PACKAGE || m_nFormat == embed::StorageFormats::OFOPXML ) )
+ rManList.push_back( aPropSet );
+
+ return bSuccess;
+}
+
+void ZipPackageStream::successfullyWritten( ZipEntry const *pEntry )
+{
+ if ( !IsPackageMember() )
+ {
+ if ( m_xStream.is() )
+ {
+ m_xStream->closeInput();
+ m_xStream.clear();
+ m_bHasSeekable = false;
+ }
+ SetPackageMember ( true );
+ }
+
+ if ( m_bRawStream )
+ {
+ // the raw stream was integrated and now behaves
+ // as usual encrypted stream
+ SetToBeEncrypted( true );
+ }
+
+ // Then copy it back afterwards...
+ aEntry = *pEntry;
+
+ // TODO/LATER: get rid of this hack ( the encrypted stream size property is changed during saving )
+ if ( m_bIsEncrypted )
+ setSize( m_nOwnStreamOrigSize );
+
+ aEntry.nOffset *= -1;
+}
+
+void ZipPackageStream::SetPackageMember( bool bNewValue )
+{
+ if ( bNewValue )
+ {
+ m_nStreamMode = PACKAGE_STREAM_PACKAGEMEMBER;
+ m_nMagicalHackPos = 0;
+ m_nMagicalHackSize = 0;
+ }
+ else if ( m_nStreamMode == PACKAGE_STREAM_PACKAGEMEMBER )
+ m_nStreamMode = PACKAGE_STREAM_NOTSET; // must be reset
+}
+
+// XActiveDataSink
+void SAL_CALL ZipPackageStream::setInputStream( const uno::Reference< io::XInputStream >& aStream )
+{
+ // if seekable access is required the wrapping will be done on demand
+ m_xStream = aStream;
+ m_nImportedEncryptionAlgorithm = 0;
+ m_bHasSeekable = false;
+ SetPackageMember ( false );
+ aEntry.nTime = -1;
+ m_nStreamMode = PACKAGE_STREAM_DETECT;
+}
+
+uno::Reference< io::XInputStream > ZipPackageStream::getRawData()
+{
+ try
+ {
+ if ( IsPackageMember() )
+ {
+ return m_rZipPackage.getZipFile().getRawData( aEntry, GetEncryptionData(), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef(), false/*bUseBufferedStream*/ );
+ }
+ else if ( GetOwnSeekStream().is() )
+ {
+ return new WrapStreamForShare( GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef() );
+ }
+ else
+ return uno::Reference < io::XInputStream > ();
+ }
+ catch ( ZipException & )//rException )
+ {
+ TOOLS_WARN_EXCEPTION( "package", "" );
+ return uno::Reference < io::XInputStream > ();
+ }
+ catch ( Exception & )
+ {
+ TOOLS_WARN_EXCEPTION( "package", "Exception is thrown during stream wrapping!" );
+ return uno::Reference < io::XInputStream > ();
+ }
+}
+
+uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getInputStream()
+{
+ try
+ {
+ if ( IsPackageMember() )
+ {
+ return m_rZipPackage.getZipFile().getInputStream( aEntry, GetEncryptionData(), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ }
+ else if ( GetOwnSeekStream().is() )
+ {
+ return new WrapStreamForShare( GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef() );
+ }
+ else
+ return uno::Reference < io::XInputStream > ();
+ }
+ catch ( ZipException & )//rException )
+ {
+ TOOLS_WARN_EXCEPTION( "package", "" );
+ return uno::Reference < io::XInputStream > ();
+ }
+ catch ( const Exception & )
+ {
+ TOOLS_WARN_EXCEPTION( "package", "Exception is thrown during stream wrapping!");
+ return uno::Reference < io::XInputStream > ();
+ }
+}
+
+// XDataSinkEncrSupport
+uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getDataStream()
+{
+ // There is no stream attached to this object
+ if ( m_nStreamMode == PACKAGE_STREAM_NOTSET )
+ return uno::Reference< io::XInputStream >();
+
+ // this method can not be used together with old approach
+ if ( m_nStreamMode == PACKAGE_STREAM_DETECT )
+ throw packages::zip::ZipIOException(THROW_WHERE );
+
+ if ( IsPackageMember() )
+ {
+ uno::Reference< io::XInputStream > xResult;
+ try
+ {
+ xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::None), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ // note: due to SHA1 check this fallback is only done for
+ // * ODF 1.2 files written by OOo < 3.4beta / LO < 3.5
+ // * ODF 1.1/OOoXML files written by any version
+ if ( m_rZipPackage.GetStartKeyGenID() == xml::crypto::DigestID::SHA1 )
+ {
+ SAL_WARN("package", "ZipPackageStream::getDataStream(): SHA1 mismatch, trying fallbacks...");
+ try
+ { // tdf#114939 try with legacy StarOffice SHA1 bug
+ xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::WrongSHA1), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ return xResult;
+ }
+ catch (const packages::WrongPasswordException&)
+ {
+ /* ignore and try next... */
+ }
+
+ try
+ {
+ // rhbz#1013844 / fdo#47482 workaround for the encrypted
+ // OpenOffice.org 1.0 documents generated by Libreoffice <=
+ // 3.6 with the new encryption format and using SHA256, but
+ // missing a specified startkey of SHA256
+
+ // force SHA256 and see if that works
+ m_nImportedStartKeyAlgorithm = xml::crypto::DigestID::SHA256;
+ xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ return xResult;
+ }
+ catch (const packages::WrongPasswordException&)
+ {
+ // if that didn't work, restore to SHA1 and trundle through the *other* earlier
+ // bug fix
+ m_nImportedStartKeyAlgorithm = xml::crypto::DigestID::SHA1;
+ }
+
+ // workaround for the encrypted documents generated with the old OOo1.x bug.
+ if ( !m_bUseWinEncoding )
+ {
+ xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::WinEncodingWrongSHA1), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ m_bUseWinEncoding = true;
+ }
+ else
+ throw;
+ }
+ else
+ throw;
+ }
+ return xResult;
+ }
+ else if ( m_nStreamMode == PACKAGE_STREAM_RAW )
+ return ZipFile::StaticGetDataFromRawStream( m_rZipPackage.GetSharedMutexRef(), m_xContext, GetOwnSeekStream(), GetEncryptionData() );
+ else if ( GetOwnSeekStream().is() )
+ {
+ return new WrapStreamForShare( GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef() );
+ }
+ else
+ return uno::Reference< io::XInputStream >();
+}
+
+uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getRawStream()
+{
+ // There is no stream attached to this object
+ if ( m_nStreamMode == PACKAGE_STREAM_NOTSET )
+ return uno::Reference< io::XInputStream >();
+
+ // this method can not be used together with old approach
+ if ( m_nStreamMode == PACKAGE_STREAM_DETECT )
+ throw packages::zip::ZipIOException(THROW_WHERE );
+
+ if ( IsPackageMember() )
+ {
+ if ( !m_bIsEncrypted || !GetEncryptionData().is() )
+ throw packages::NoEncryptionException(THROW_WHERE );
+
+ return m_rZipPackage.getZipFile().getWrappedRawStream( aEntry, GetEncryptionData(), msMediaType, m_rZipPackage.GetSharedMutexRef() );
+ }
+ else if ( GetOwnSeekStream().is() )
+ {
+ if ( m_nStreamMode == PACKAGE_STREAM_RAW )
+ {
+ return new WrapStreamForShare( GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef() );
+ }
+ else if ( m_nStreamMode == PACKAGE_STREAM_DATA && m_bToBeEncrypted )
+ return TryToGetRawFromDataStream( true );
+ }
+
+ throw packages::NoEncryptionException(THROW_WHERE );
+}
+
+void SAL_CALL ZipPackageStream::setDataStream( const uno::Reference< io::XInputStream >& aStream )
+{
+ setInputStream( aStream );
+ m_nStreamMode = PACKAGE_STREAM_DATA;
+}
+
+void SAL_CALL ZipPackageStream::setRawStream( const uno::Reference< io::XInputStream >& aStream )
+{
+ // wrap the stream in case it is not seekable
+ uno::Reference< io::XInputStream > xNewStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( aStream, m_xContext );
+ uno::Reference< io::XSeekable > xSeek( xNewStream, UNO_QUERY_THROW );
+ xSeek->seek( 0 );
+ uno::Reference< io::XInputStream > xOldStream = m_xStream;
+ m_xStream = xNewStream;
+ if ( !ParsePackageRawStream() )
+ {
+ m_xStream = xOldStream;
+ throw packages::NoRawFormatException(THROW_WHERE );
+ }
+
+ // the raw stream MUST have seekable access
+ m_bHasSeekable = true;
+
+ SetPackageMember ( false );
+ aEntry.nTime = -1;
+ m_nStreamMode = PACKAGE_STREAM_RAW;
+}
+
+uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getPlainRawStream()
+{
+ // There is no stream attached to this object
+ if ( m_nStreamMode == PACKAGE_STREAM_NOTSET )
+ return uno::Reference< io::XInputStream >();
+
+ // this method can not be used together with old approach
+ if ( m_nStreamMode == PACKAGE_STREAM_DETECT )
+ throw packages::zip::ZipIOException(THROW_WHERE );
+
+ if ( IsPackageMember() )
+ {
+ return m_rZipPackage.getZipFile().getRawData( aEntry, GetEncryptionData(), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ }
+ else if ( GetOwnSeekStream().is() )
+ {
+ if ( m_nStreamMode == PACKAGE_STREAM_RAW )
+ {
+ // the header should not be returned here
+ return GetRawEncrStreamNoHeaderCopy();
+ }
+ else if ( m_nStreamMode == PACKAGE_STREAM_DATA )
+ return TryToGetRawFromDataStream( false );
+ }
+
+ return uno::Reference< io::XInputStream >();
+}
+
+// XPropertySet
+void SAL_CALL ZipPackageStream::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ if ( aPropertyName == "MediaType" )
+ {
+ if ( m_rZipPackage.getFormat() != embed::StorageFormats::PACKAGE && m_rZipPackage.getFormat() != embed::StorageFormats::OFOPXML )
+ throw beans::PropertyVetoException(THROW_WHERE );
+
+ if ( !(aValue >>= msMediaType) )
+ throw IllegalArgumentException(THROW_WHERE "MediaType must be a string!",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ if ( !msMediaType.isEmpty() )
+ {
+ if ( msMediaType.indexOf ( "text" ) != -1
+ || msMediaType == "application/vnd.sun.star.oleobject" )
+ m_bToBeCompressed = true;
+ else if ( !m_bCompressedIsSetFromOutside )
+ m_bToBeCompressed = false;
+ }
+ }
+ else if ( aPropertyName == "Size" )
+ {
+ if ( !( aValue >>= aEntry.nSize ) )
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for Size property!",
+ uno::Reference< XInterface >(),
+ 2 );
+ }
+ else if ( aPropertyName == "Encrypted" )
+ {
+ if ( m_rZipPackage.getFormat() != embed::StorageFormats::PACKAGE )
+ throw beans::PropertyVetoException(THROW_WHERE );
+
+ bool bEnc = false;
+ if ( !(aValue >>= bEnc) )
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for Encrypted property!",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ // In case of new raw stream, the stream must not be encrypted on storing
+ if ( bEnc && m_nStreamMode == PACKAGE_STREAM_RAW )
+ throw IllegalArgumentException(THROW_WHERE "Raw stream can not be encrypted on storing",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ m_bToBeEncrypted = bEnc;
+ if ( m_bToBeEncrypted && !m_xBaseEncryptionData.is() )
+ m_xBaseEncryptionData = new BaseEncryptionData;
+
+ }
+ else if ( aPropertyName == ENCRYPTION_KEY_PROPERTY )
+ {
+ if ( m_rZipPackage.getFormat() != embed::StorageFormats::PACKAGE )
+ throw beans::PropertyVetoException(THROW_WHERE );
+
+ uno::Sequence< sal_Int8 > aNewKey;
+
+ if ( !( aValue >>= aNewKey ) )
+ {
+ OUString sTempString;
+ if ( !(aValue >>= sTempString) )
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for EncryptionKey property!",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ sal_Int32 nPathLength = sTempString.getLength();
+ Sequence < sal_Int8 > aSequence ( nPathLength );
+ sal_Int8 *pArray = aSequence.getArray();
+ const sal_Unicode *pChar = sTempString.getStr();
+ for ( sal_Int32 i = 0; i < nPathLength; i++ )
+ pArray[i] = static_cast < sal_Int8 > ( pChar[i] );
+ aNewKey = aSequence;
+ }
+
+ if ( aNewKey.hasElements() )
+ {
+ if ( !m_xBaseEncryptionData.is() )
+ m_xBaseEncryptionData = new BaseEncryptionData;
+
+ m_aEncryptionKey = aNewKey;
+ // In case of new raw stream, the stream must not be encrypted on storing
+ m_bHaveOwnKey = true;
+ if ( m_nStreamMode != PACKAGE_STREAM_RAW )
+ m_bToBeEncrypted = true;
+ }
+ else
+ {
+ m_bHaveOwnKey = false;
+ m_aEncryptionKey.realloc( 0 );
+ }
+
+ m_aStorageEncryptionKeys.realloc( 0 );
+ }
+ else if ( aPropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
+ {
+ if ( m_rZipPackage.getFormat() != embed::StorageFormats::PACKAGE )
+ throw beans::PropertyVetoException(THROW_WHERE );
+
+ uno::Sequence< beans::NamedValue > aKeys;
+ if ( !( aValue >>= aKeys ) )
+ {
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for StorageEncryptionKeys property!",
+ uno::Reference< XInterface >(),
+ 2 );
+ }
+
+ if ( aKeys.hasElements() )
+ {
+ if ( !m_xBaseEncryptionData.is() )
+ m_xBaseEncryptionData = new BaseEncryptionData;
+
+ m_aStorageEncryptionKeys = aKeys;
+
+ // In case of new raw stream, the stream must not be encrypted on storing
+ m_bHaveOwnKey = true;
+ if ( m_nStreamMode != PACKAGE_STREAM_RAW )
+ m_bToBeEncrypted = true;
+ }
+ else
+ {
+ m_bHaveOwnKey = false;
+ m_aStorageEncryptionKeys.realloc( 0 );
+ }
+
+ m_aEncryptionKey.realloc( 0 );
+ }
+ else if ( aPropertyName == "Compressed" )
+ {
+ bool bCompr = false;
+
+ if ( !(aValue >>= bCompr) )
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for Compressed property!",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ // In case of new raw stream, the stream must not be encrypted on storing
+ if ( bCompr && m_nStreamMode == PACKAGE_STREAM_RAW )
+ throw IllegalArgumentException(THROW_WHERE "Raw stream can not be encrypted on storing",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ m_bToBeCompressed = bCompr;
+ m_bCompressedIsSetFromOutside = true;
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+Any SAL_CALL ZipPackageStream::getPropertyValue( const OUString& PropertyName )
+{
+ if ( PropertyName == "MediaType" )
+ {
+ return Any(msMediaType);
+ }
+ else if ( PropertyName == "Size" )
+ {
+ return Any(aEntry.nSize);
+ }
+ else if ( PropertyName == "Encrypted" )
+ {
+ return Any((m_nStreamMode == PACKAGE_STREAM_RAW) || m_bToBeEncrypted);
+ }
+ else if ( PropertyName == "WasEncrypted" )
+ {
+ return Any(m_bIsEncrypted);
+ }
+ else if ( PropertyName == "Compressed" )
+ {
+ return Any(m_bToBeCompressed);
+ }
+ else if ( PropertyName == ENCRYPTION_KEY_PROPERTY )
+ {
+ return Any(m_aEncryptionKey);
+ }
+ else if ( PropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
+ {
+ return Any(m_aStorageEncryptionKeys);
+ }
+ else
+ throw beans::UnknownPropertyException(PropertyName);
+}
+
+void ZipPackageStream::setSize ( const sal_Int64 nNewSize )
+{
+ if ( aEntry.nCompressedSize != nNewSize )
+ aEntry.nMethod = DEFLATED;
+ aEntry.nSize = nNewSize;
+}
+OUString ZipPackageStream::getImplementationName()
+{
+ return "ZipPackageStream";
+}
+
+Sequence< OUString > ZipPackageStream::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.PackageStream" };
+}
+
+sal_Bool SAL_CALL ZipPackageStream::supportsService( OUString const & rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/wrapstreamforshare.cxx b/package/source/zippackage/wrapstreamforshare.cxx
new file mode 100644
index 0000000000..250ccb4ba5
--- /dev/null
+++ b/package/source/zippackage/wrapstreamforshare.cxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/io/IOException.hpp>
+#include <utility>
+#include <osl/diagnose.h>
+
+#include "wrapstreamforshare.hxx"
+
+using namespace ::com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+WrapStreamForShare::WrapStreamForShare( uno::Reference< io::XInputStream > xInStream,
+ rtl::Reference< comphelper::RefCountedMutex > xMutexRef )
+: m_xMutex(std::move( xMutexRef ))
+, m_xInStream(std::move( xInStream ))
+, m_nCurPos( 0 )
+{
+ if ( !m_xMutex.is() || !m_xInStream.is() )
+ {
+ OSL_FAIL( "Wrong initialization of wrapping stream!" );
+ throw uno::RuntimeException(THROW_WHERE );
+ }
+ m_xSeekable.set( m_xInStream, uno::UNO_QUERY_THROW );
+}
+
+WrapStreamForShare::~WrapStreamForShare()
+{
+}
+
+// XInputStream
+sal_Int32 SAL_CALL WrapStreamForShare::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ m_xSeekable->seek( m_nCurPos );
+
+ sal_Int32 nRead = m_xInStream->readBytes( aData, nBytesToRead );
+ m_nCurPos += nRead;
+
+ return nRead;
+}
+
+sal_Int32 SAL_CALL WrapStreamForShare::readSomeBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ m_xSeekable->seek( m_nCurPos );
+
+ sal_Int32 nRead = m_xInStream->readSomeBytes( aData, nMaxBytesToRead );
+ m_nCurPos += nRead;
+
+ return nRead;
+}
+
+void SAL_CALL WrapStreamForShare::skipBytes( sal_Int32 nBytesToSkip )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ m_xSeekable->seek( m_nCurPos );
+
+ m_xInStream->skipBytes( nBytesToSkip );
+ m_nCurPos = m_xSeekable->getPosition();
+}
+
+sal_Int32 SAL_CALL WrapStreamForShare::available()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return m_xInStream->available();
+}
+
+void SAL_CALL WrapStreamForShare::closeInput()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ // the package is the owner so it will close the stream
+ // m_xInStream->closeInput();
+ m_xInStream.clear();
+ m_xSeekable.clear();
+}
+
+// XSeekable
+void SAL_CALL WrapStreamForShare::seek( sal_Int64 location )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ // let stream implementation do all the checking
+ m_xSeekable->seek( location );
+
+ m_nCurPos = m_xSeekable->getPosition();
+}
+
+sal_Int64 SAL_CALL WrapStreamForShare::getPosition()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return m_nCurPos;
+}
+
+sal_Int64 SAL_CALL WrapStreamForShare::getLength()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return m_xSeekable->getLength();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/wrapstreamforshare.hxx b/package/source/zippackage/wrapstreamforshare.hxx
new file mode 100644
index 0000000000..ac8de25884
--- /dev/null
+++ b/package/source/zippackage/wrapstreamforshare.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPPACKAGE_WRAPSTREAMFORSHARE_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPPACKAGE_WRAPSTREAMFORSHARE_HXX
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <comphelper/refcountedmutex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+class WrapStreamForShare final : public cppu::WeakImplHelper < css::io::XInputStream
+ , css::io::XSeekable >
+{
+ rtl::Reference< comphelper::RefCountedMutex > m_xMutex;
+ css::uno::Reference < css::io::XInputStream > m_xInStream;
+ css::uno::Reference < css::io::XSeekable > m_xSeekable;
+
+ sal_Int64 m_nCurPos;
+
+public:
+ WrapStreamForShare( css::uno::Reference< css::io::XInputStream > xInStream,
+ rtl::Reference< comphelper::RefCountedMutex > xMutexRef );
+ virtual ~WrapStreamForShare() override;
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+
+ //XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+ virtual sal_Int64 SAL_CALL getPosition() override;
+ virtual sal_Int64 SAL_CALL getLength() override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/zipfileaccess.cxx b/package/source/zippackage/zipfileaccess.cxx
new file mode 100644
index 0000000000..d693a51cec
--- /dev/null
+++ b/package/source/zippackage/zipfileaccess.cxx
@@ -0,0 +1,479 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <zipfileaccess.hxx>
+#include "ZipPackageSink.hxx"
+#include <EncryptionData.hxx>
+
+#include <ucbhelper/content.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+OZipFileAccess::OZipFileAccess( const uno::Reference< uno::XComponentContext >& rxContext )
+: m_aMutexHolder( new comphelper::RefCountedMutex )
+, m_xContext( rxContext )
+, m_bDisposed( false )
+, m_bOwnContent( false )
+{
+ if ( !rxContext.is() )
+ throw uno::RuntimeException(THROW_WHERE );
+}
+
+OZipFileAccess::~OZipFileAccess()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+ if ( !m_bDisposed )
+ {
+ try {
+ // dispose will use refcounting so the further destruction must be avoided
+ osl_atomic_increment(&m_refCount);
+ dispose();
+ } catch( uno::Exception& )
+ {}
+ }
+}
+
+uno::Sequence< OUString > OZipFileAccess::GetPatternsFromString_Impl( const OUString& aString )
+{
+ if ( aString.isEmpty() )
+ return uno::Sequence< OUString >();
+
+ uno::Sequence< OUString > aPattern( 1 );
+ auto pPattern = aPattern.getArray();
+ sal_Int32 nInd = 0;
+
+ const sal_Unicode* pString = aString.getStr();
+ while( *pString )
+ {
+ if ( *pString == '\\' )
+ {
+ pString++;
+
+ if ( *pString == '\\' )
+ {
+ pPattern[nInd] += "\\";
+ pString++;
+ }
+ else if ( *pString == '*' )
+ {
+ pPattern[nInd] += "*";
+ pString++;
+ }
+ else
+ {
+ OSL_FAIL( "The backslash is not guarded!" );
+ pPattern[nInd] += "\\";
+ }
+ }
+ else if ( *pString == '*' )
+ {
+ aPattern.realloc( ( ++nInd ) + 1 );
+ pPattern = aPattern.getArray();
+ pString++;
+ }
+ else
+ {
+ pPattern[nInd] += OUStringChar( *pString );
+ pString++;
+ }
+ }
+
+ return aPattern;
+}
+
+bool OZipFileAccess::StringGoodForPattern_Impl( std::u16string_view aString,
+ const uno::Sequence< OUString >& aPattern )
+{
+ sal_Int32 nInd = aPattern.getLength() - 1;
+ if ( nInd < 0 )
+ return false;
+
+ if ( nInd == 0 )
+ {
+ if ( aPattern[0].isEmpty() )
+ return true;
+
+ return aString == aPattern[0];
+ }
+
+ sal_Int32 nBeginInd = aPattern[0].getLength();
+ sal_Int32 nEndInd = aString.size() - aPattern[nInd].getLength();
+ if ( nEndInd < nBeginInd
+ || ( nEndInd != sal_Int32(aString.size()) && aString.substr( nEndInd ) != aPattern[nInd] )
+ || ( nBeginInd != 0 && aString.substr( 0, nBeginInd ) != aPattern[0] ) )
+ return false;
+
+ for ( sal_Int32 nCurInd = aPattern.getLength() - 2; nCurInd > 0; nCurInd-- )
+ {
+ if ( aPattern[nCurInd].isEmpty() )
+ continue;
+
+ if ( nEndInd == nBeginInd )
+ return false;
+
+ // check that search does not use nEndInd position
+ size_t nLastInd = aString.substr(0, nEndInd - 1).rfind( aPattern[nCurInd] );
+
+ if ( nLastInd == std::u16string_view::npos )
+ return false;
+
+ if ( sal_Int32(nLastInd) < nBeginInd )
+ return false;
+
+ nEndInd = nLastInd;
+ }
+
+ return true;
+}
+
+// XInitialization
+void SAL_CALL OZipFileAccess::initialize( const uno::Sequence< uno::Any >& aArguments )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE ); // initialization is allowed only one time
+
+ if ( !aArguments.hasElements() )
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ OSL_ENSURE( aArguments.getLength() == 1, "Too many arguments are provided, only the first one will be used!" );
+
+ OUString aParamURL;
+ uno::Reference< io::XStream > xStream;
+ uno::Reference< io::XSeekable > xSeekable;
+ uno::Sequence<beans::NamedValue> aArgs;
+
+ auto openInputStream = [&]()
+ {
+ ::ucbhelper::Content aContent(
+ aParamURL,
+ uno::Reference< css::ucb::XCommandEnvironment >(),
+ m_xContext );
+ uno::Reference < io::XActiveDataSink > xSink = new ZipPackageSink;
+ if ( aContent.openStream ( xSink ) )
+ {
+ m_xContentStream = xSink->getInputStream();
+ m_bOwnContent = true;
+ xSeekable.set( m_xContentStream, uno::UNO_QUERY );
+ }
+ };
+
+ if ( aArguments[0] >>= aParamURL )
+ {
+ openInputStream();
+ }
+ else if ( aArguments[0] >>= xStream )
+ {
+ // a writable stream can implement both XStream & XInputStream
+ m_xContentStream = xStream->getInputStream();
+ xSeekable.set( xStream, uno::UNO_QUERY );
+ }
+ else if ( aArguments[0] >>= m_xContentStream )
+ {
+ xSeekable.set( m_xContentStream, uno::UNO_QUERY );
+ }
+ else if (aArguments[0] >>= aArgs)
+ {
+ for (const beans::NamedValue& rArg : std::as_const(aArgs))
+ {
+ if (rArg.Name == "URL")
+ rArg.Value >>= aParamURL;
+ }
+
+ if (aParamURL.isEmpty())
+ throw lang::IllegalArgumentException(
+ THROW_WHERE"required argument 'URL' is not given or invalid.",
+ uno::Reference<uno::XInterface>(), 1);
+
+ openInputStream();
+ }
+ else
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ if ( !m_xContentStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ if ( !xSeekable.is() )
+ {
+ // TODO: after fwkbugfix02 is integrated a helper class can be used to make the stream seekable
+ throw io::IOException(THROW_WHERE );
+ }
+
+ // TODO: in case xSeekable is implemented on separated XStream implementation a wrapper is required
+ m_pZipFile.emplace(
+ m_aMutexHolder,
+ m_xContentStream,
+ m_xContext,
+ true );
+}
+
+// XNameAccess
+uno::Any SAL_CALL OZipFileAccess::getByName( const OUString& aName )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ EntryHash::iterator aIter = m_pZipFile->GetEntryHash().find( aName );
+ if ( aIter == m_pZipFile->GetEntryHash().end() )
+ throw container::NoSuchElementException(THROW_WHERE );
+
+ uno::Reference< io::XInputStream > xEntryStream;
+ try
+ {
+ xEntryStream = m_pZipFile->getDataStream((*aIter).second,
+ ::rtl::Reference< EncryptionData >(),
+ false,
+ m_aMutexHolder);
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ throw;
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetException( "This package is unusable!",
+ getXWeak(), anyEx);
+ }
+
+ if ( !xEntryStream.is() )
+ throw uno::RuntimeException(THROW_WHERE );
+
+ return uno::Any ( xEntryStream );
+}
+
+uno::Sequence< OUString > SAL_CALL OZipFileAccess::getElementNames()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ uno::Sequence< OUString > aNames( m_pZipFile->GetEntryHash().size() );
+ auto pNames = aNames.getArray();
+ sal_Int32 nLen = 0;
+
+ for ( const auto& rEntry : m_pZipFile->GetEntryHash() )
+ {
+ if ( aNames.getLength() < ++nLen )
+ {
+ OSL_FAIL( "The size must be the same!" );
+ aNames.realloc( nLen );
+ pNames = aNames.getArray();
+ }
+
+ pNames[nLen-1] = rEntry.second.sPath;
+ }
+
+ if ( aNames.getLength() != nLen )
+ {
+ OSL_FAIL( "The size must be the same!" );
+ aNames.realloc( nLen );
+ }
+
+ return aNames;
+}
+
+sal_Bool SAL_CALL OZipFileAccess::hasByName( const OUString& aName )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ EntryHash::iterator aIter = m_pZipFile->GetEntryHash().find( aName );
+
+ return ( aIter != m_pZipFile->GetEntryHash().end() );
+}
+
+uno::Type SAL_CALL OZipFileAccess::getElementType()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ return cppu::UnoType<io::XInputStream>::get();
+}
+
+sal_Bool SAL_CALL OZipFileAccess::hasElements()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ return ( !m_pZipFile->GetEntryHash().empty() );
+}
+
+// XZipFileAccess
+uno::Reference< io::XInputStream > SAL_CALL OZipFileAccess::getStreamByPattern( const OUString& aPatternString )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw io::NotConnectedException(THROW_WHERE );
+
+ // Code to compare strings by patterns
+ uno::Sequence< OUString > aPattern = GetPatternsFromString_Impl( aPatternString );
+
+ auto aIter = std::find_if(m_pZipFile->GetEntryHash().begin(), m_pZipFile->GetEntryHash().end(),
+ [&aPattern](const EntryHash::value_type& rEntry) { return StringGoodForPattern_Impl(rEntry.second.sPath, aPattern); });
+ if (aIter != m_pZipFile->GetEntryHash().end())
+ {
+ uno::Reference< io::XInputStream > xEntryStream( m_pZipFile->getDataStream( (*aIter).second,
+ ::rtl::Reference< EncryptionData >(),
+ false,
+ m_aMutexHolder ) );
+
+ if ( !xEntryStream.is() )
+ throw uno::RuntimeException(THROW_WHERE );
+ return xEntryStream;
+ }
+
+ throw container::NoSuchElementException(THROW_WHERE );
+}
+
+// XComponent
+void SAL_CALL OZipFileAccess::dispose()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( m_pListenersContainer )
+ {
+ lang::EventObject aSource( getXWeak() );
+ m_pListenersContainer->disposeAndClear( aSource );
+ m_pListenersContainer.reset();
+ }
+
+ m_pZipFile.reset();
+
+ if ( m_xContentStream.is() && m_bOwnContent )
+ try {
+ m_xContentStream->closeInput();
+ } catch( uno::Exception& )
+ {}
+
+ m_bDisposed = true;
+}
+
+void SAL_CALL OZipFileAccess::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pListenersContainer )
+ m_pListenersContainer.reset( new ::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>( m_aMutexHolder->GetMutex() ) );
+ m_pListenersContainer->addInterface( xListener );
+}
+
+void SAL_CALL OZipFileAccess::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( m_pListenersContainer )
+ m_pListenersContainer->removeInterface( xListener );
+}
+
+OUString SAL_CALL OZipFileAccess::getImplementationName()
+{
+ return "com.sun.star.comp.package.zip.ZipFileAccess";
+}
+
+sal_Bool SAL_CALL OZipFileAccess::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL OZipFileAccess::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.zip.ZipFileAccess",
+ "com.sun.star.comp.packages.zip.ZipFileAccess" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+package_OZipFileAccess_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new OZipFileAccess(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/util/package2.component b/package/util/package2.component
new file mode 100644
index 0000000000..d436d9a35a
--- /dev/null
+++ b/package/util/package2.component
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.package.zip.ZipFileAccess"
+ constructor="package_OZipFileAccess_get_implementation">
+ <service name="com.sun.star.comp.packages.zip.ZipFileAccess"/>
+ <service name="com.sun.star.packages.zip.ZipFileAccess"/>
+ </implementation>
+ <implementation name="com.sun.star.packages.comp.ZipPackage"
+ constructor="package_ZipPackage_get_implementation">
+ <service name="com.sun.star.packages.Package"/>
+ </implementation>
+ <implementation name="com.sun.star.packages.manifest.comp.ManifestReader"
+ constructor="package_ManifestReader_get_implementation">
+ <service name="com.sun.star.packages.manifest.ManifestReader"/>
+ </implementation>
+ <implementation name="com.sun.star.packages.manifest.comp.ManifestWriter"
+ constructor="package_ManifestWriter_get_implementation">
+ <service name="com.sun.star.packages.manifest.ManifestWriter"/>
+ </implementation>
+</component>