tdf#105844 package: ODF wholesome encryption: use package version

... to init the Version property.

The problem is that the outer storage loaded from a wholesome ODF
encrypted document doesn't have a Version, because it doesn't (directly)
contain a document and has no "/" file-entry.

Extract the root element's package version attribute and use it.

The Storage API doesn't distinguish much between the package version and
the root document's (i.e. root folder's) version.

Change-Id: I0fd5f999e9adee674d73fc542402512d0e204897
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161897
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
diff --git a/package/source/manifest/ManifestImport.cxx b/package/source/manifest/ManifestImport.cxx
index 0218ac0..77f795e 100644
--- a/package/source/manifest/ManifestImport.cxx
+++ b/package/source/manifest/ManifestImport.cxx
@@ -327,6 +327,7 @@ void SAL_CALL ManifestImport::startElement( const OUString& aName, const uno::Re

    switch (nLevel) {
    case 1: {
        m_PackageVersion = aConvertedAttribs[ATTRIBUTE_VERSION];
        if (aConvertedName != ELEMENT_MANIFEST) //manifest:manifest
            aStack.back().m_bValid = false;
        break;
@@ -451,6 +452,18 @@ void SAL_CALL ManifestImport::endElement( const OUString& aName )
        return;

    if ( aConvertedName == ELEMENT_FILE_ENTRY && aStack.back().m_bValid ) {
        // required for wholesome encryption: if there is no document and hence
        // no file-entry with a version attribute, send the package's version
        // with the first file-entry.
        // (note: the only case when a valid ODF document has no "/" entry with
        // a version is when it is ODF 1.0/1.1 and then it doesn't have the
        // package version either)
        if (rManVector.empty() && !m_PackageVersion.isEmpty()
            && !aSequence[PKG_MNFST_VERSION].Value.hasValue())
        {
            aSequence[PKG_MNFST_VERSION].Name = u"Version"_ustr;
            aSequence[PKG_MNFST_VERSION].Value <<= m_PackageVersion;
        }
        // the first entry gets KeyInfo element if any, for PGP encryption
        if (!bIgnoreEncryptData && !aKeys.empty() && rManVector.empty())
        {
diff --git a/package/source/manifest/ManifestImport.hxx b/package/source/manifest/ManifestImport.hxx
index fd86e02..883f1de 100644
--- a/package/source/manifest/ManifestImport.hxx
+++ b/package/source/manifest/ManifestImport.hxx
@@ -61,6 +61,7 @@ class ManifestImport final : public cppu::WeakImplHelper < css::xml::sax::XDocum
    bool bPgpEncryption;
    sal_Int32 nDerivedKeySize;
    ::std::vector < css::uno::Sequence < css::beans::PropertyValue > > & rManVector;
    OUString m_PackageVersion; // on root element


    OUString PushNameAndNamespaces( const OUString& aName,
diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx
index 02f7cf7..c4de219 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -171,6 +171,7 @@ void ZipPackage::parseManifest()
        return;

    bool bManifestParsed = false;
    ::std::optional<OUString> oFirstVersion;
    static constexpr OUString sMeta (u"META-INF"_ustr);
    if ( m_xRootFolder->hasByName( sMeta ) )
    {
@@ -216,7 +217,13 @@ void ZipPackage::parseManifest()
                            if ( rValue.Name == sPropFullPath )
                                rValue.Value >>= sPath;
                            else if ( rValue.Name == sPropVersion )
                            {
                                rValue.Value >>= sVersion;
                                if (!oFirstVersion)
                                {
                                    oFirstVersion.emplace(sVersion);
                                }
                            }
                            else if ( rValue.Name == sPropMediaType )
                                rValue.Value >>= sMediaType;
                            else if ( rValue.Name == sPropSalt )
@@ -457,6 +464,11 @@ void ZipPackage::parseManifest()
            {
                // accept only types that look similar to own mediatypes
                m_xRootFolder->SetMediaType( aPackageMediatype );
                // also set version explicitly
                if (oFirstVersion && m_xRootFolder->GetVersion().isEmpty())
                {
                    m_xRootFolder->SetVersion(*oFirstVersion);
                }
                // if there is an encrypted inner package, there is no root
                // document, because instead there is a package, and it is not
                // an error