summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editeng/source/items/textitem.cxx48
-rw-r--r--embeddedobj/source/commonembedding/miscobj.cxx68
-rw-r--r--embeddedobj/source/commonembedding/persistence.cxx5
-rw-r--r--embeddedobj/source/commonembedding/specialobject.cxx18
-rw-r--r--embeddedobj/source/general/dummyobject.cxx16
-rw-r--r--embeddedobj/source/inc/commonembobj.hxx17
-rw-r--r--embeddedobj/source/inc/dummyobject.hxx8
-rw-r--r--embeddedobj/source/inc/oleembobj.hxx9
-rw-r--r--embeddedobj/source/inc/specialobject.hxx5
-rw-r--r--embeddedobj/source/msole/olemisc.cxx16
-rw-r--r--filter/qa/unit/data/semi-transparent-multi-para.odgbin0 -> 10299 bytes
-rw-r--r--filter/qa/unit/svg.cxx29
-rw-r--r--filter/source/svg/svgwriter.cxx5
-rw-r--r--include/editeng/fontitem.hxx1
-rw-r--r--include/sfx2/ipclient.hxx2
-rw-r--r--include/svl/sigstruct.hxx39
-rw-r--r--include/svtools/embedhlp.hxx4
-rw-r--r--include/tools/stream.hxx4
-rw-r--r--include/unotest/macros_test.hxx9
-rw-r--r--include/vcl/errinf.hxx7
-rw-r--r--include/xmloff/xmlimp.hxx6
-rw-r--r--include/xmloff/xmlnmspe.hxx10
-rw-r--r--include/xmloff/xmltoken.hxx13
-rw-r--r--sfx2/source/doc/sfxbasemodel.cxx11
-rw-r--r--sfx2/source/view/ipclient.cxx4
-rw-r--r--svl/source/crypto/cryptosign.cxx16
-rw-r--r--svtools/source/misc/embedhlp.cxx35
-rw-r--r--sw/CppunitTest_sw_core_view.mk71
-rw-r--r--sw/CppunitTest_sw_htmlexport.mk1
-rw-r--r--sw/CppunitTest_sw_uibase_shells.mk1
-rw-r--r--sw/Module_sw.mk1
-rw-r--r--sw/inc/ndole.hxx3
-rw-r--r--sw/inc/numrule.hxx3
-rw-r--r--sw/inc/swcli.hxx2
-rw-r--r--sw/inc/viewsh.hxx4
-rw-r--r--sw/qa/core/unocore/data/broken-embedded-object.odtbin0 -> 14360 bytes
-rw-r--r--sw/qa/core/unocore/unocore.cxx31
-rw-r--r--sw/qa/core/view/data/update-ole-object-previews.odtbin0 -> 10726 bytes
-rw-r--r--sw/qa/core/view/view.cxx71
-rw-r--r--sw/qa/extras/htmlexport/data/transparent.pngbin0 -> 2950 bytes
-rw-r--r--sw/qa/extras/htmlexport/htmlexport.cxx270
-rw-r--r--sw/qa/extras/layout/data/tdf134298.ottbin0 -> 40880 bytes
-rw-r--r--sw/qa/extras/layout/data/tdf138039.odtbin0 -> 29534 bytes
-rw-r--r--sw/qa/extras/layout/layout.cxx45
-rw-r--r--sw/qa/uibase/shells/data/ole-preview-update.odtbin0 -> 20265 bytes
-rw-r--r--sw/qa/uibase/shells/shells.cxx23
-rw-r--r--sw/source/core/doc/number.cxx23
-rw-r--r--sw/source/core/edit/edfcol.cxx3
-rw-r--r--sw/source/core/inc/pagefrm.hxx3
-rw-r--r--sw/source/core/layout/layact.cxx20
-rw-r--r--sw/source/core/layout/pagechg.cxx108
-rw-r--r--sw/source/core/layout/tabfrm.cxx36
-rw-r--r--sw/source/core/layout/wsfrm.cxx16
-rw-r--r--sw/source/core/ole/ndole.cxx32
-rw-r--r--sw/source/core/unocore/unoframe.cxx20
-rw-r--r--sw/source/core/unocore/unoparagraph.cxx16
-rw-r--r--sw/source/core/view/viewsh.cxx41
-rw-r--r--sw/source/filter/html/css1atr.cxx76
-rw-r--r--sw/source/filter/html/css1kywd.cxx1
-rw-r--r--sw/source/filter/html/css1kywd.hxx1
-rw-r--r--sw/source/filter/html/htmlatr.cxx30
-rw-r--r--sw/source/filter/html/htmlflywriter.cxx6
-rw-r--r--sw/source/filter/html/htmlnumwriter.cxx57
-rw-r--r--sw/source/filter/html/htmlplug.cxx3
-rw-r--r--sw/source/filter/html/htmlreqifreader.cxx8
-rw-r--r--sw/source/filter/html/htmltabw.cxx27
-rw-r--r--sw/source/filter/html/wrthtml.cxx142
-rw-r--r--sw/source/filter/html/wrthtml.hxx32
-rw-r--r--sw/source/filter/xml/xmlfonte.cxx18
-rw-r--r--sw/source/uibase/app/docsh.cxx16
-rw-r--r--sw/source/uibase/docvw/edtwin.cxx14
-rw-r--r--sw/source/uibase/shells/basesh.cxx1
-rw-r--r--sw/source/uibase/uiview/swcli.cxx25
-rw-r--r--sw/source/uibase/uiview/view.cxx3
-rw-r--r--sw/source/uibase/wrtsh/wrtsh1.cxx10
-rw-r--r--tools/CppunitTest_tools_test.mk1
-rw-r--r--tools/qa/cppunit/test_stream.cxx33
-rw-r--r--tools/source/stream/stream.cxx8
-rw-r--r--unotest/Library_unotest.mk1
-rw-r--r--unotest/source/cpp/macros_test.cxx17
-rw-r--r--vcl/source/window/errinf.cxx15
-rw-r--r--xmloff/CppunitTest_xmloff_text.mk2
-rw-r--r--xmloff/qa/unit/data/list-id.fodt23
-rw-r--r--xmloff/qa/unit/style.cxx53
-rw-r--r--xmloff/qa/unit/text.cxx39
-rw-r--r--xmloff/source/core/xmlimp.cxx26
-rw-r--r--xmloff/source/core/xmltoken.cxx13
-rw-r--r--xmloff/source/text/XMLTextNumRuleInfo.cxx9
-rw-r--r--xmloff/source/text/XMLTextNumRuleInfo.hxx4
-rw-r--r--xmloff/source/text/txtparae.cxx4
-rw-r--r--xmloff/source/token/tokens.txt10
-rw-r--r--xmlsecurity/inc/biginteger.hxx3
-rw-r--r--xmlsecurity/inc/xmlsec-wrapper.h4
-rw-r--r--xmlsecurity/inc/xmlsignaturehelper.hxx12
-rw-r--r--xmlsecurity/inc/xsecctl.hxx20
-rw-r--r--xmlsecurity/source/component/documentdigitalsignatures.cxx73
-rw-r--r--xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx15
-rw-r--r--xmlsecurity/source/helper/documentsignaturehelper.cxx63
-rw-r--r--xmlsecurity/source/helper/documentsignaturemanager.cxx12
-rw-r--r--xmlsecurity/source/helper/ooxmlsecexporter.cxx22
-rw-r--r--xmlsecurity/source/helper/ooxmlsecparser.cxx1457
-rw-r--r--xmlsecurity/source/helper/ooxmlsecparser.hxx74
-rw-r--r--xmlsecurity/source/helper/pdfsignaturehelper.cxx8
-rw-r--r--xmlsecurity/source/helper/xmlsignaturehelper.cxx162
-rw-r--r--xmlsecurity/source/helper/xsecctl.cxx82
-rw-r--r--xmlsecurity/source/helper/xsecparser.cxx1699
-rw-r--r--xmlsecurity/source/helper/xsecparser.hxx106
-rw-r--r--xmlsecurity/source/helper/xsecsign.cxx34
-rw-r--r--xmlsecurity/source/helper/xsecverify.cxx182
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx4
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx47
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx6
-rw-r--r--xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx25
-rw-r--r--xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx7
114 files changed, 5159 insertions, 935 deletions
diff --git a/editeng/source/items/textitem.cxx b/editeng/source/items/textitem.cxx
index 2e2cf4fe7604..e6817adb9a8d 100644
--- a/editeng/source/items/textitem.cxx
+++ b/editeng/source/items/textitem.cxx
@@ -153,6 +153,24 @@ bool SvxFontListItem::GetPresentation
// class SvxFontItem -----------------------------------------------------
+namespace
+{
+sal_Int32 CompareTo(sal_Int32 nA, sal_Int32 nB)
+{
+ if (nA < nB)
+ {
+ return -1;
+ }
+
+ if (nA > nB)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+}
+
SvxFontItem::SvxFontItem( const sal_uInt16 nId ) :
SfxPoolItem( nId )
{
@@ -290,6 +308,36 @@ bool SvxFontItem::operator==( const SfxPoolItem& rAttr ) const
return bRet;
}
+bool SvxFontItem::operator<(const SfxPoolItem& rCmp) const
+{
+ const auto& rOther = static_cast<const SvxFontItem&>(rCmp);
+ sal_Int32 nRet = GetFamilyName().compareTo(rOther.GetFamilyName());
+ if (nRet != 0)
+ {
+ return nRet < 0;
+ }
+
+ nRet = GetStyleName().compareTo(rOther.GetStyleName());
+ if (nRet != 0)
+ {
+ return nRet < 0;
+ }
+
+ nRet = CompareTo(GetFamily(), rOther.GetFamily());
+ if (nRet != 0)
+ {
+ return nRet < 0;
+ }
+
+ nRet = CompareTo(GetPitch(), rOther.GetPitch());
+ if (nRet != 0)
+ {
+ return nRet < 0;
+ }
+
+ return GetCharSet() < rOther.GetCharSet();
+}
+
SvxFontItem* SvxFontItem::Clone( SfxItemPool * ) const
{
return new SvxFontItem( *this );
diff --git a/embeddedobj/source/commonembedding/miscobj.cxx b/embeddedobj/source/commonembedding/miscobj.cxx
index ea2a3cb3c110..3f4414b8118a 100644
--- a/embeddedobj/source/commonembedding/miscobj.cxx
+++ b/embeddedobj/source/commonembedding/miscobj.cxx
@@ -32,6 +32,8 @@
#include <comphelper/mimeconfighelper.hxx>
#include <vcl/svapp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequenceashashmap.hxx>
#include "persistence.hxx"
@@ -341,6 +343,21 @@ uno::Any SAL_CALL OCommonEmbeddedObject::queryInterface( const uno::Type& rType
void* p = static_cast<embed::XEmbedPersist2*>(this);
return uno::Any(&p, rType);
}
+ else if (rType == cppu::UnoType<lang::XServiceInfo>::get())
+ {
+ void* p = static_cast<lang::XServiceInfo*>(this);
+ return uno::Any(&p, rType);
+ }
+ else if (rType == cppu::UnoType<lang::XInitialization>::get())
+ {
+ void* p = static_cast<lang::XInitialization*>(this);
+ return uno::Any(&p, rType);
+ }
+ else if (rType == cppu::UnoType<lang::XTypeProvider>::get())
+ {
+ void* p = static_cast<lang::XTypeProvider*>(this);
+ return uno::Any(&p, rType);
+ }
else
aReturn = ::cppu::queryInterface(
rType,
@@ -583,4 +600,55 @@ void SAL_CALL OCommonEmbeddedObject::removeEventListener( const uno::Reference<
xListener );
}
+OUString SAL_CALL OCommonEmbeddedObject::getImplementationName()
+{
+ return "com.sun.star.comp.embed.OCommonEmbeddedObject";
+}
+
+sal_Bool SAL_CALL OCommonEmbeddedObject::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL OCommonEmbeddedObject::getSupportedServiceNames()
+{
+ return { "com.sun.star.comp.embed.OCommonEmbeddedObject" };
+}
+
+uno::Sequence<uno::Type> SAL_CALL OCommonEmbeddedObject::getTypes()
+{
+ static const uno::Sequence<uno::Type> aTypes{
+ cppu::UnoType<embed::XEmbeddedObject>::get(),
+ cppu::UnoType<embed::XEmbedPersist2>::get(),
+ cppu::UnoType<embed::XLinkageSupport>::get(),
+ cppu::UnoType<embed::XInplaceObject>::get(),
+ cppu::UnoType<container::XChild>::get(),
+ cppu::UnoType<chart2::XDefaultSizeTransmitter>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XInitialization>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ };
+ return aTypes;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL OCommonEmbeddedObject::getImplementationId()
+{
+ return uno::Sequence<sal_Int8>();
+}
+
+void SAL_CALL OCommonEmbeddedObject::initialize(const uno::Sequence<uno::Any>& rArguments)
+{
+ if (!rArguments.hasElements())
+ {
+ return;
+ }
+
+ comphelper::SequenceAsHashMap aMap(rArguments[0]);
+ auto it = aMap.find("ReadOnly");
+ if (it != aMap.end())
+ {
+ it->second >>= m_bReadOnly;
+ }
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/embeddedobj/source/commonembedding/persistence.cxx b/embeddedobj/source/commonembedding/persistence.cxx
index 3e4ab0dd989f..9618c2776de6 100644
--- a/embeddedobj/source/commonembedding/persistence.cxx
+++ b/embeddedobj/source/commonembedding/persistence.cxx
@@ -530,6 +530,11 @@ uno::Reference< util::XCloseable > OCommonEmbeddedObject::LoadDocumentFromStorag
// set the document mode to embedded as the first step!!!
EmbedAndReparentDoc_Impl( xDocument );
+ if (m_bReadOnly)
+ {
+ aLoadArgs.put("ReadOnly", true);
+ }
+
if ( xDoc.is() )
{
xDoc->loadFromStorage( xSourceStorage, aLoadArgs.getPropertyValues() );
diff --git a/embeddedobj/source/commonembedding/specialobject.cxx b/embeddedobj/source/commonembedding/specialobject.cxx
index 683fe0aab3f2..809ffce1f2c4 100644
--- a/embeddedobj/source/commonembedding/specialobject.cxx
+++ b/embeddedobj/source/commonembedding/specialobject.cxx
@@ -28,6 +28,7 @@
#include <cppuhelper/queryinterface.hxx>
#include <osl/diagnose.h>
+#include <cppuhelper/supportsservice.hxx>
#include <specialobject.hxx>
@@ -51,6 +52,8 @@ uno::Any SAL_CALL OSpecialEmbeddedObject::queryInterface( const uno::Type& rType
static_cast< embed::XClassifiedObject* >( this ),
static_cast< embed::XComponentSupplier* >( this ),
static_cast< util::XCloseable* >( this ),
+ static_cast< lang::XServiceInfo* >( this ),
+ static_cast< lang::XTypeProvider* >( this ),
static_cast< document::XEventBroadcaster* >( this ) );
if ( aReturn.hasValue() )
return aReturn;
@@ -160,4 +163,19 @@ void SAL_CALL OSpecialEmbeddedObject::doVerb( sal_Int32 nVerbID )
OCommonEmbeddedObject::doVerb( nVerbID );
}
+OUString SAL_CALL OSpecialEmbeddedObject::getImplementationName()
+{
+ return "com.sun.star.comp.embed.OSpecialEmbeddedObject";
+}
+
+sal_Bool SAL_CALL OSpecialEmbeddedObject::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL OSpecialEmbeddedObject::getSupportedServiceNames()
+{
+ return { "com.sun.star.comp.embed.OSpecialEmbeddedObject" };
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/embeddedobj/source/general/dummyobject.cxx b/embeddedobj/source/general/dummyobject.cxx
index e37aca939b9b..cb251571222b 100644
--- a/embeddedobj/source/general/dummyobject.cxx
+++ b/embeddedobj/source/general/dummyobject.cxx
@@ -30,6 +30,7 @@
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/lang/NoSupportException.hpp>
+#include <cppuhelper/supportsservice.hxx>
#include <dummyobject.hxx>
@@ -620,4 +621,19 @@ void SAL_CALL ODummyEmbeddedObject::removeEventListener( const uno::Reference< d
xListener );
}
+OUString SAL_CALL ODummyEmbeddedObject::getImplementationName()
+{
+ return "com.sun.star.comp.embed.ODummyEmbeddedObject";
+}
+
+sal_Bool SAL_CALL ODummyEmbeddedObject::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ODummyEmbeddedObject::getSupportedServiceNames()
+{
+ return { "com.sun.star.comp.embed.ODummyEmbeddedObject" };
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/embeddedobj/source/inc/commonembobj.hxx b/embeddedobj/source/inc/commonembobj.hxx
index 61e939a0c62f..88fccb37bc40 100644
--- a/embeddedobj/source/inc/commonembobj.hxx
+++ b/embeddedobj/source/inc/commonembobj.hxx
@@ -33,6 +33,8 @@
#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/util/XCloseable.hpp>
#include <com/sun/star/chart2/XDefaultSizeTransmitter.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
#include <cppuhelper/weak.hxx>
#include <rtl/ref.hxx>
#include <map>
@@ -78,6 +80,9 @@ class OCommonEmbeddedObject : public css::embed::XEmbeddedObject
, public css::embed::XInplaceObject
, public css::container::XChild
, public css::chart2::XDefaultSizeTransmitter
+ , public css::lang::XServiceInfo
+ , public css::lang::XInitialization
+ , public css::lang::XTypeProvider
, public ::cppu::OWeakObject
{
protected:
@@ -394,6 +399,18 @@ public:
// XDefaultSizeTransmitter
//#i103460# charts do not necessarily have an own size within ODF files, in this case they need to use the size settings from the surrounding frame, which is made available with this method
virtual void SAL_CALL setDefaultSize( const css::awt::Size& rSize_100TH_MM ) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XInitialization
+ void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override;
+
+ // XTypeProvider
+ css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override;
+ css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
};
#endif
diff --git a/embeddedobj/source/inc/dummyobject.hxx b/embeddedobj/source/inc/dummyobject.hxx
index 60bca914b636..e8004cbcf28e 100644
--- a/embeddedobj/source/inc/dummyobject.hxx
+++ b/embeddedobj/source/inc/dummyobject.hxx
@@ -28,6 +28,7 @@
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/interfacecontainer.hxx>
@@ -54,7 +55,8 @@ namespace cppu {
*/
class ODummyEmbeddedObject : public ::cppu::WeakImplHelper
< css::embed::XEmbeddedObject
- , css::embed::XEmbedPersist >
+ , css::embed::XEmbedPersist
+ , css::lang::XServiceInfo >
{
::osl::Mutex m_aMutex;
std::unique_ptr<cppu::OMultiTypeInterfaceContainerHelper>
@@ -199,6 +201,10 @@ public:
virtual void SAL_CALL removeEventListener(
const css::uno::Reference< css::document::XEventListener >& Listener ) override;
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
};
#endif
diff --git a/embeddedobj/source/inc/oleembobj.hxx b/embeddedobj/source/inc/oleembobj.hxx
index 051d63294af4..f1480e08d71e 100644
--- a/embeddedobj/source/inc/oleembobj.hxx
+++ b/embeddedobj/source/inc/oleembobj.hxx
@@ -34,6 +34,7 @@
#include <com/sun/star/util/XCloseListener.hpp>
#include <com/sun/star/io/XActiveDataStreamer.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
#include <cppuhelper/implbase.hxx>
#include <rtl/ref.hxx>
@@ -115,7 +116,8 @@ class OleEmbeddedObject : public ::cppu::WeakImplHelper
, css::embed::XInplaceObject
, css::container::XChild
, css::io::XActiveDataStreamer
- , css::lang::XInitialization >
+ , css::lang::XInitialization
+ , css::lang::XServiceInfo >
{
friend class OleComponent;
@@ -445,6 +447,11 @@ public:
// XInitialization
void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
};
#endif
diff --git a/embeddedobj/source/inc/specialobject.hxx b/embeddedobj/source/inc/specialobject.hxx
index 32ad61a7a582..695aad730cd9 100644
--- a/embeddedobj/source/inc/specialobject.hxx
+++ b/embeddedobj/source/inc/specialobject.hxx
@@ -48,6 +48,11 @@ public:
virtual void SAL_CALL changeState( sal_Int32 nNewState ) override;
virtual void SAL_CALL doVerb( sal_Int32 nVerbID ) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
};
#endif
diff --git a/embeddedobj/source/msole/olemisc.cxx b/embeddedobj/source/msole/olemisc.cxx
index 6de6a9cdca75..c87812fc73fc 100644
--- a/embeddedobj/source/msole/olemisc.cxx
+++ b/embeddedobj/source/msole/olemisc.cxx
@@ -30,6 +30,7 @@
#include <cppuhelper/interfacecontainer.h>
#include <comphelper/sequenceashashmap.hxx>
+#include <cppuhelper/supportsservice.hxx>
#include <oleembobj.hxx>
#include "olepersist.hxx"
@@ -688,4 +689,19 @@ void OleEmbeddedObject::initialize(const uno::Sequence<uno::Any>& rArguments)
}
}
+OUString SAL_CALL OleEmbeddedObject::getImplementationName()
+{
+ return "com.sun.star.comp.embed.OleEmbeddedObject";
+}
+
+sal_Bool SAL_CALL OleEmbeddedObject::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL OleEmbeddedObject::getSupportedServiceNames()
+{
+ return { "com.sun.star.comp.embed.OleEmbeddedObject" };
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/qa/unit/data/semi-transparent-multi-para.odg b/filter/qa/unit/data/semi-transparent-multi-para.odg
new file mode 100644
index 000000000000..5e01e31e4f7f
--- /dev/null
+++ b/filter/qa/unit/data/semi-transparent-multi-para.odg
Binary files differ
diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx
index 5dcb1af0eb90..070225b1491d 100644
--- a/filter/qa/unit/svg.cxx
+++ b/filter/qa/unit/svg.cxx
@@ -14,6 +14,10 @@
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+
#include <unotools/streamwrap.hxx>
#include <unotools/mediadescriptor.hxx>
#include <tools/stream.hxx>
@@ -160,6 +164,31 @@ CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentText)
assertXPath(pXmlDoc, "//svg:text[2]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity]", 0);
}
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentMultiParaText)
+{
+ // Given a shape with semi-transparent, multi-paragraph text:
+ load(u"semi-transparent-multi-para.odg");
+
+ // When exporting to SVG:
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export");
+ aMediaDescriptor["OutputStream"] <<= xOut;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Then make sure that the two semi-tranparent paragraphs have the same X position:
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ assertXPath(pXmlDoc, "(//svg:tspan[@class='TextPosition'])[1]", "x", "5908");
+ assertXPath(pXmlDoc, "(//svg:tspan[@class='TextPosition'])[1]/svg:tspan", "fill-opacity",
+ "0.8");
+ assertXPath(pXmlDoc, "(//svg:tspan[@class='TextPosition'])[2]", "x", "5908");
+ assertXPath(pXmlDoc, "(//svg:tspan[@class='TextPosition'])[2]/svg:tspan", "fill-opacity",
+ "0.8");
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 3e54df31ba49..c3abf3420877 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -640,6 +640,11 @@ sal_Int32 SVGTextWriter::setTextPosition(const GDIMetaFile& rMtf, sal_uLong& nCu
{
// Text is found in the inner metafile.
bConfigured = true;
+
+ // nTextFound == 1 is only possible if the inner setTextPosition() had bEmpty ==
+ // false, adjust our bEmpty accordingly.
+ bEmpty = false;
+
mrActionWriter.StartMask(pA->GetPoint(), pA->GetSize(), pA->GetGradient(),
nWriteFlags, &maTextOpacity);
}
diff --git a/include/editeng/fontitem.hxx b/include/editeng/fontitem.hxx
index 9a73a051f79e..2ccaade20121 100644
--- a/include/editeng/fontitem.hxx
+++ b/include/editeng/fontitem.hxx
@@ -46,6 +46,7 @@ public:
// "pure virtual Methods" from SfxPoolItem
virtual bool operator==(const SfxPoolItem& rItem) const override;
+ bool operator<(const SfxPoolItem& rCmp) const override;
virtual SvxFontItem* Clone(SfxItemPool *pPool = nullptr) const override;
virtual bool QueryValue(css::uno::Any& rVal, sal_uInt8 nMemberId = 0) const override;
virtual bool PutValue(const css::uno::Any& rVal, sal_uInt8 nMemberId) override;
diff --git a/include/sfx2/ipclient.hxx b/include/sfx2/ipclient.hxx
index 62c421c3c783..a7a85270c5e0 100644
--- a/include/sfx2/ipclient.hxx
+++ b/include/sfx2/ipclient.hxx
@@ -87,6 +87,8 @@ public:
bool IsUIActive() const;
virtual void FormatChanged(); // object format was changed (used for StarMath formulas aligning)
+
+ virtual bool IsProtected() const;
};
#endif
diff --git a/include/svl/sigstruct.hxx b/include/svl/sigstruct.hxx
index 38c1ee5ed142..6d53e048d47b 100644
--- a/include/svl/sigstruct.hxx
+++ b/include/svl/sigstruct.hxx
@@ -89,9 +89,30 @@ struct SignatureInformation
sal_Int32 nSecurityId;
css::xml::crypto::SecurityOperationStatus nStatus;
SignatureReferenceInformations vSignatureReferenceInfors;
- OUString ouX509IssuerName;
- OUString ouX509SerialNumber;
- OUString ouX509Certificate;
+ struct X509CertInfo
+ {
+ OUString X509IssuerName;
+ OUString X509SerialNumber;
+ OUString X509Certificate;
+ /// OOXML certificate SHA-256 digest, empty for ODF except when doing XAdES signature.
+ OUString CertDigest;
+ /// The certificate owner (aka subject).
+ OUString X509Subject;
+ };
+ typedef std::vector<X509CertInfo> X509Data;
+ // note: at parse time, it's unkown which one is the signing certificate;
+ // ImplVerifySignatures() figures it out and puts it at the back
+ std::vector<X509Data> X509Datas;
+
+ X509CertInfo const* GetSigningCertificate() const
+ {
+ if (X509Datas.empty())
+ {
+ return nullptr;
+ }
+ assert(!X509Datas.back().empty());
+ return & X509Datas.back().back();
+ }
OUString ouGpgKeyID;
OUString ouGpgCertificate;
@@ -103,6 +124,9 @@ struct SignatureInformation
// XAdES EncapsulatedX509Certificate values
std::set<OUString> maEncapsulatedX509Certificates;
+ OUString ouSignatureId;
+ // signature may contain multiple time stamps - check they're consistent
+ bool hasInconsistentSigningTime = false;
//We also keep the date and time as string. This is done when this
//structure is created as a result of a XML signature being read.
//When then a signature is added or another removed, then the original
@@ -115,14 +139,12 @@ struct SignatureInformation
//and the converted time is written back, then the string looks different
//and the signature is broken.
OUString ouDateTime;
- OUString ouSignatureId;
- OUString ouPropertyId;
+ /// The Id attribute of the <SignatureProperty> element that contains the <dc:date>.
+ OUString ouDateTimePropertyId;
/// Characters of the <dc:description> element inside the signature.
OUString ouDescription;
/// The Id attribute of the <SignatureProperty> element that contains the <dc:description>.
OUString ouDescriptionPropertyId;
- /// OOXML certificate SHA-256 digest, empty for ODF except when doing XAdES signature.
- OUString ouCertDigest;
/// Valid and invalid signature line images
css::uno::Reference<css::graphic::XGraphic> aValidSignatureImage;
css::uno::Reference<css::graphic::XGraphic> aInvalidSignatureImage;
@@ -137,9 +159,6 @@ struct SignatureInformation
/// For PDF: the byte range doesn't cover the whole document.
bool bPartialDocumentSignature;
- /// The certificate owner (aka subject).
- OUString ouSubject;
-
svl::crypto::SignatureMethodAlgorithm eAlgorithmID;
SignatureInformation( sal_Int32 nId )
diff --git a/include/svtools/embedhlp.hxx b/include/svtools/embedhlp.hxx
index 12a8a2367a4b..09addf47f026 100644
--- a/include/svtools/embedhlp.hxx
+++ b/include/svtools/embedhlp.hxx
@@ -108,6 +108,10 @@ public:
bool is() const;
bool IsLocked() const;
+
+ void SetIsProtectedHdl(const Link<LinkParamNone*, bool>& rProtectedHdl);
+ Link<LinkParamNone*, bool> GetIsProtectedHdl() const;
+
bool IsChart() const;
OUString GetChartType();
diff --git a/include/tools/stream.hxx b/include/tools/stream.hxx
index 4cbe1a3e930c..99d7fa13b2b5 100644
--- a/include/tools/stream.hxx
+++ b/include/tools/stream.hxx
@@ -661,6 +661,10 @@ public:
void SetBuffer( void* pBuf, std::size_t nSize, std::size_t nEOF );
void ObjectOwnsMemory( bool bOwn ) { bOwnsData = bOwn; }
+ /// Makes the stream read-only after it was (possibly) initially writable,
+ /// without having to copy the data or change buffers.
+ /// @since LibreOffice 7.5
+ void MakeReadOnly();
void SetResizeOffset( std::size_t nNewResize ) { nResize = nNewResize; }
virtual sal_uInt64 TellEnd() override { FlushBuffer(); return nEndOfData; }
};
diff --git a/include/unotest/macros_test.hxx b/include/unotest/macros_test.hxx
index c60ea1fe97ce..7c0c5cbf6cae 100644
--- a/include/unotest/macros_test.hxx
+++ b/include/unotest/macros_test.hxx
@@ -26,6 +26,11 @@ struct TestMacroInfo
};
class BasicDLL;
+class SvStream;
+namespace utl
+{
+class TempFile;
+}
namespace unotest {
@@ -43,6 +48,10 @@ public:
const OUString& rCommand,
const css::uno::Sequence<css::beans::PropertyValue>& rPropertyValues);
+ /// Opens rStreamName from rTempFile, assuming it's a ZIP storage.
+ static std::unique_ptr<SvStream> parseExportStream(const utl::TempFile& rTempFile,
+ const OUString& rStreamName);
+
protected:
css::uno::Reference< css::frame::XDesktop2> mxDesktop;
diff --git a/include/vcl/errinf.hxx b/include/vcl/errinf.hxx
index 4a0edc75b259..705e6d97cde4 100644
--- a/include/vcl/errinf.hxx
+++ b/include/vcl/errinf.hxx
@@ -59,11 +59,18 @@ public:
static void RegisterDisplay(BasicDisplayErrorFunc*);
static void RegisterDisplay(WindowDisplayErrorFunc*);
+
+ static void SetLock(bool bLock);
+ static bool GetLock();
+
static void Reset();
private:
DisplayFnPtr pDsp;
bool bIsWindowDsp;
+
+ bool m_bLock;
+
sal_uInt16 nNextError;
std::vector<ErrorHandler*> errorHandlers;
diff --git a/include/xmloff/xmlimp.hxx b/include/xmloff/xmlimp.hxx
index 351a05887ebd..247eb001f9dd 100644
--- a/include/xmloff/xmlimp.hxx
+++ b/include/xmloff/xmlimp.hxx
@@ -240,8 +240,12 @@ class XMLOFF_DLLPUBLIC SvXMLImport : public cppu::WeakImplHelper<
static void initializeNamespaceMaps();
void registerNamespaces();
- std::unique_ptr<SvXMLNamespaceMap> processNSAttributes(
+public:
+ static std::unique_ptr<SvXMLNamespaceMap> processNSAttributes(
+ std::unique_ptr<SvXMLNamespaceMap> & rpNamespaceMap,
+ SvXMLImport *const pImport,
const css::uno::Reference< css::xml::sax::XAttributeList >& xAttrList);
+private:
void Characters(const OUString& aChars);
css::uno::Reference< css::task::XStatusIndicator > mxStatusIndicator;
diff --git a/include/xmloff/xmlnmspe.hxx b/include/xmloff/xmlnmspe.hxx
index d45832f02d81..91ff2aae4cf3 100644
--- a/include/xmloff/xmlnmspe.hxx
+++ b/include/xmloff/xmlnmspe.hxx
@@ -69,6 +69,16 @@ constexpr sal_uInt16 XML_NAMESPACE_TCD = 34; // text conversion di
constexpr sal_uInt16 XML_NAMESPACE_DLG = 35;
constexpr sal_uInt16 XML_NAMESPACE_REPORT = 36;
constexpr sal_uInt16 XML_NAMESPACE_VERSIONS_LIST = 37;
+// OOo extension digital signatures, used in ODF 1.1
+constexpr sal_uInt16 XML_NAMESPACE_DSIG_OOO = 38;
+// ODF 1.2 digital signature namespaces
+constexpr sal_uInt16 XML_NAMESPACE_DSIG = 39;
+constexpr sal_uInt16 XML_NAMESPACE_DS = 40;
+constexpr sal_uInt16 XML_NAMESPACE_XADES132 = 41;
+constexpr sal_uInt16 XML_NAMESPACE_XADES141 = 42;
+// OOXML digital signature extension namespaces, also based on xmldsig-core
+constexpr sal_uInt16 XML_NAMESPACE_MDSSI = 43;
+constexpr sal_uInt16 XML_NAMESPACE_MSODIGSIG = 44;
// namespaces for ODF extended formats
constexpr sal_uInt16 XML_NAMESPACE_EXT_BASE = 50;
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index 49178ebdc996..b6956245ed17 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -135,6 +135,19 @@ namespace xmloff::token {
XML_NP_GRDDL,
XML_N_GRDDL,
+ // OOo extension digital signatures, used in ODF 1.1
+ XML_NP_DSIG_OOO,
+ XML_N_DSIG_OOO,
+ // ODF 1.2 digital signatures
+ XML_NP_DSIG,
+ XML_N_DSIG,
+ XML_NP_DS,
+ XML_N_DS,
+ XML_NP_XADES132,
+ XML_N_XADES132,
+ XML_NP_XADES141,
+ XML_N_XADES141,
+
// ODF Enhanced namespaces
XML_NP_OFFICE_EXT,
XML_N_OFFICE_EXT,
diff --git a/sfx2/source/doc/sfxbasemodel.cxx b/sfx2/source/doc/sfxbasemodel.cxx
index 533650751d75..adcea5aaf752 100644
--- a/sfx2/source/doc/sfxbasemodel.cxx
+++ b/sfx2/source/doc/sfxbasemodel.cxx
@@ -1680,7 +1680,16 @@ void SAL_CALL SfxBaseModel::storeAsURL( const OUString& rURL
SfxSaveGuard aSaveGuard(this, m_pData.get());
- impl_store( rURL, rArgs, false );
+ utl::MediaDescriptor aDescriptor(rArgs);
+ bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false);
+ if (bOnMainThread)
+ {
+ vcl::solarthread::syncExecute([this, rURL, rArgs]() { impl_store(rURL, rArgs, false); });
+ }
+ else
+ {
+ impl_store(rURL, rArgs, false);
+ }
Sequence< beans::PropertyValue > aSequence ;
TransformItems( SID_OPENDOC, *m_pData->m_pObjectShell->GetMedium()->GetItemSet(), aSequence );
diff --git a/sfx2/source/view/ipclient.cxx b/sfx2/source/view/ipclient.cxx
index 5dc6267e5c94..d98ade7bd398 100644
--- a/sfx2/source/view/ipclient.cxx
+++ b/sfx2/source/view/ipclient.cxx
@@ -217,7 +217,7 @@ uno::Reference < frame::XFrame > const & SfxInPlaceClient_Impl::GetFrame() const
void SAL_CALL SfxInPlaceClient_Impl::saveObject()
{
- if ( !m_bStoreObject )
+ if (!m_bStoreObject || m_pClient->IsProtected())
// client wants to discard the object (usually it means the container document is closed while an object is active
// and the user didn't request saving the changes
return;
@@ -1040,6 +1040,8 @@ void SfxInPlaceClient::FormatChanged()
// dummy implementation
}
+bool SfxInPlaceClient::IsProtected() const { return false; }
+
void SfxInPlaceClient::DeactivateObject()
{
if ( !GetObject().is() )
diff --git a/svl/source/crypto/cryptosign.cxx b/svl/source/crypto/cryptosign.cxx
index 573c06ba5826..a015f00576a4 100644
--- a/svl/source/crypto/cryptosign.cxx
+++ b/svl/source/crypto/cryptosign.cxx
@@ -2094,8 +2094,12 @@ bool Signing::Verify(const std::vector<unsigned char>& aData,
aDerCert[i] = pCertificate->derCert.data[i];
OUStringBuffer aBuffer;
comphelper::Base64::encode(aBuffer, aDerCert);
- rInformation.ouX509Certificate = aBuffer.makeStringAndClear();
- rInformation.ouSubject = OUString(pCertificate->subjectName, PL_strlen(pCertificate->subjectName), RTL_TEXTENCODING_UTF8);
+ SignatureInformation::X509Data temp;
+ temp.emplace_back();
+ temp.back().X509Certificate = aBuffer.makeStringAndClear();
+ temp.back().X509Subject = OUString(pCertificate->subjectName, PL_strlen(pCertificate->subjectName), RTL_TEXTENCODING_UTF8);
+ rInformation.X509Datas.clear();
+ rInformation.X509Datas.emplace_back(temp);
}
PRTime nSigningTime;
@@ -2274,8 +2278,12 @@ bool Signing::Verify(const std::vector<unsigned char>& aData,
aDerCert[i] = pSignerCertContext->pbCertEncoded[i];
OUStringBuffer aBuffer;
comphelper::Base64::encode(aBuffer, aDerCert);
- rInformation.ouX509Certificate = aBuffer.makeStringAndClear();
- rInformation.ouSubject = GetSubjectName(pSignerCertContext);
+ SignatureInformation::X509Data temp;
+ temp.emplace_back();
+ temp.back().X509Certificate = aBuffer.makeStringAndClear();
+ temp.back().X509Subject = GetSubjectName(pSignerCertContext);
+ rInformation.X509Datas.clear();
+ rInformation.X509Datas.emplace_back(temp);
}
if (bNonDetached)
diff --git a/svtools/source/misc/embedhlp.cxx b/svtools/source/misc/embedhlp.cxx
index f8dcf1356621..e329ccd9d14b 100644
--- a/svtools/source/misc/embedhlp.cxx
+++ b/svtools/source/misc/embedhlp.cxx
@@ -135,9 +135,16 @@ void SAL_CALL EmbedEventListener_Impl::stateChanged( const lang::EventObject&,
uno::Reference < util::XModifiable > xMod( pObject->GetObject()->getComponent(), uno::UNO_QUERY );
if ( nNewState == embed::EmbedStates::RUNNING )
{
+ bool bProtected = false;
+ if (pObject->GetIsProtectedHdl().IsSet())
+ {
+ bProtected = pObject->GetIsProtectedHdl().Call(nullptr);
+ }
+
// TODO/LATER: container must be set before!
// When is this event created? Who sets the new container when it changed?
- if( ( pObject->GetViewAspect() != embed::Aspects::MSOLE_ICON ) && nOldState != embed::EmbedStates::LOADED && !pObject->IsChart() )
+ if ((pObject->GetViewAspect() != embed::Aspects::MSOLE_ICON)
+ && nOldState != embed::EmbedStates::LOADED && !pObject->IsChart() && !bProtected)
// get new replacement after deactivation
pObject->UpdateReplacement();
@@ -239,6 +246,8 @@ struct EmbeddedObjectRef_Impl
sal_uInt32 mnGraphicVersion;
awt::Size aDefaultSizeForChart_In_100TH_MM;//#i103460# charts do not necessarily have an own size within ODF files, in this case they need to use the size settings from the surrounding frame, which is made available with this member
+ Link<LinkParamNone*, bool> m_aIsProtectedHdl;
+
EmbeddedObjectRef_Impl() :
pContainer(nullptr),
nViewAspect(embed::Aspects::MSOLE_CONTENT),
@@ -394,6 +403,16 @@ bool EmbeddedObjectRef::IsLocked() const
return mpImpl->bIsLocked;
}
+void EmbeddedObjectRef::SetIsProtectedHdl(const Link<LinkParamNone*, bool>& rProtectedHdl)
+{
+ mpImpl->m_aIsProtectedHdl = rProtectedHdl;
+}
+
+Link<LinkParamNone*, bool> EmbeddedObjectRef::GetIsProtectedHdl() const
+{
+ return mpImpl->m_aIsProtectedHdl;
+}
+
void EmbeddedObjectRef::GetReplacement( bool bUpdate )
{
Graphic aOldGraphic;
@@ -420,6 +439,15 @@ void EmbeddedObjectRef::GetReplacement( bool bUpdate )
}
std::unique_ptr<SvStream> pGraphicStream(GetGraphicStream( bUpdate ));
+ if (!pGraphicStream && aOldGraphic.IsNone())
+ {
+ // We have no old graphic, tried to get an updated one, but that failed. Try to get an old
+ // graphic instead of having no graphic at all.
+ pGraphicStream = GetGraphicStream(false);
+ SAL_WARN("svtools.misc",
+ "EmbeddedObjectRef::GetReplacement: failed to get updated graphic stream");
+ }
+
if ( pGraphicStream )
{
GraphicFilter& rGF = GraphicFilter::GetGraphicFilter();
@@ -436,7 +464,7 @@ void EmbeddedObjectRef::GetReplacement( bool bUpdate )
// failed. Go back to the old graphic instead of having no graphic at
// all.
mpImpl->pGraphic.reset(new Graphic(aOldGraphic));
- SAL_WARN("svtools.misc", "EmbeddedObjectRef::GetReplacement: update failed");
+ SAL_WARN("svtools.misc", "EmbeddedObjectRef::GetReplacement: failed to update graphic");
}
}
@@ -571,7 +599,7 @@ std::unique_ptr<SvStream> EmbeddedObjectRef::GetGraphicStream( bool bUpdate ) co
if ( xStream.is() )
{
const sal_Int32 nConstBufferSize = 32000;
- std::unique_ptr<SvStream> pStream(new SvMemoryStream( 32000, 32000 ));
+ std::unique_ptr<SvMemoryStream> pStream(new SvMemoryStream( 32000, 32000 ));
try
{
sal_Int32 nRead=0;
@@ -583,6 +611,7 @@ std::unique_ptr<SvStream> EmbeddedObjectRef::GetGraphicStream( bool bUpdate ) co
}
while ( nRead == nConstBufferSize );
pStream->Seek(0);
+ pStream->MakeReadOnly();
return pStream;
}
catch (const uno::Exception&)
diff --git a/sw/CppunitTest_sw_core_view.mk b/sw/CppunitTest_sw_core_view.mk
new file mode 100644
index 000000000000..e8529467c10c
--- /dev/null
+++ b/sw/CppunitTest_sw_core_view.mk
@@ -0,0 +1,71 @@
+# -*- 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,sw_core_view))
+
+$(eval $(call gb_CppunitTest_use_common_precompiled_header,sw_core_view))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sw_core_view, \
+ sw/qa/core/view/view \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sw_core_view, \
+ cppu \
+ cppuhelper \
+ sal \
+ svl \
+ svt \
+ sw \
+ test \
+ unotest \
+ utl \
+ vcl \
+ sfx \
+ comphelper \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,sw_core_view,\
+ boost_headers \
+ libxml2 \
+))
+
+$(eval $(call gb_CppunitTest_set_include,sw_core_view,\
+ -I$(SRCDIR)/sw/inc \
+ -I$(SRCDIR)/sw/source/core/inc \
+ -I$(SRCDIR)/sw/source/uibase/inc \
+ -I$(SRCDIR)/sw/qa/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_api,sw_core_view,\
+ udkapi \
+ offapi \
+ oovbaapi \
+))
+
+$(eval $(call gb_CppunitTest_use_ure,sw_core_view))
+$(eval $(call gb_CppunitTest_use_vcl,sw_core_view))
+
+$(eval $(call gb_CppunitTest_use_rdb,sw_core_view,services))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,sw_core_view,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,sw_core_view))
+
+$(eval $(call gb_CppunitTest_use_uiconfigs,sw_core_view, \
+ modules/swriter \
+))
+
+$(eval $(call gb_CppunitTest_use_more_fonts,sw_core_view))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sw/CppunitTest_sw_htmlexport.mk b/sw/CppunitTest_sw_htmlexport.mk
index 3315566bb087..efa4a82d807f 100644
--- a/sw/CppunitTest_sw_htmlexport.mk
+++ b/sw/CppunitTest_sw_htmlexport.mk
@@ -21,6 +21,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sw_htmlexport, \
comphelper \
cppu \
cppuhelper \
+ editeng \
i18nlangtag \
msfilter \
sal \
diff --git a/sw/CppunitTest_sw_uibase_shells.mk b/sw/CppunitTest_sw_uibase_shells.mk
index 9734b395bca6..a3b913ab572c 100644
--- a/sw/CppunitTest_sw_uibase_shells.mk
+++ b/sw/CppunitTest_sw_uibase_shells.mk
@@ -32,6 +32,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sw_uibase_shells, \
unotest \
utl \
vcl \
+ tl \
))
$(eval $(call gb_CppunitTest_use_externals,sw_uibase_shells,\
diff --git a/sw/Module_sw.mk b/sw/Module_sw.mk
index 723c26f53e40..50e7d11cd9d9 100644
--- a/sw/Module_sw.mk
+++ b/sw/Module_sw.mk
@@ -120,6 +120,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sw,\
CppunitTest_sw_core_unocore \
CppunitTest_sw_core_crsr \
CppunitTest_sw_uibase_uiview \
+ CppunitTest_sw_core_view \
))
ifneq ($(DISABLE_GUI),TRUE)
diff --git a/sw/inc/ndole.hxx b/sw/inc/ndole.hxx
index 8382a42e58dc..e8c7506184f3 100644
--- a/sw/inc/ndole.hxx
+++ b/sw/inc/ndole.hxx
@@ -52,6 +52,8 @@ class SW_DLLPUBLIC SwOLEObj
void SetNode( SwOLENode* pNode );
+ DECL_LINK(IsProtectedHdl, LinkParamNone*, bool);
+
public:
SwOLEObj( const svt::EmbeddedObjectRef& pObj );
SwOLEObj( const OUString &rName, sal_Int64 nAspect );
@@ -69,6 +71,7 @@ public:
const OUString& GetCurrentPersistName() const { return m_aName; }
OUString GetStyleString();
bool IsOleRef() const; ///< To avoid unnecessary loading of object.
+ bool IsProtected() const;
// try to get OLE visualization in form of a Primitive2DSequence
// and the corresponding B2DRange. This data may be locally buffered
diff --git a/sw/inc/numrule.hxx b/sw/inc/numrule.hxx
index 6152e6bee99f..b0bf138afb33 100644
--- a/sw/inc/numrule.hxx
+++ b/sw/inc/numrule.hxx
@@ -270,6 +270,9 @@ public:
void dumpAsXml(xmlTextWriterPtr w) const;
void GetGrabBagItem(css::uno::Any& rVal) const;
void SetGrabBagItem(const css::uno::Any& rVal);
+
+ /// Is it possible that this numbering has multiple lists?
+ bool HasContinueList() const;
};
/// namespace for static functions and methods for numbering and bullets
diff --git a/sw/inc/swcli.hxx b/sw/inc/swcli.hxx
index 0a6b65a33897..fc77d0885d1e 100644
--- a/sw/inc/swcli.hxx
+++ b/sw/inc/swcli.hxx
@@ -44,6 +44,8 @@ public:
bool IsCheckForOLEInCaption() const { return m_IsOldCheckForOLEInCaption; }
virtual void FormatChanged() override;
+
+ bool IsProtected() const override;
};
#endif
diff --git a/sw/inc/viewsh.hxx b/sw/inc/viewsh.hxx
index fe3fee74bcea..72b361203824 100644
--- a/sw/inc/viewsh.hxx
+++ b/sw/inc/viewsh.hxx
@@ -370,6 +370,10 @@ public:
// All about fields.
void UpdateFields(bool bCloseDB = false);
bool IsAnyFieldInDoc() const;
+
+ /// Update the previews of all OLE objects.
+ void UpdateOleObjectPreviews();
+
// Update all charts, for that exists any table.
void UpdateAllCharts();
bool HasCharts() const;
diff --git a/sw/qa/core/unocore/data/broken-embedded-object.odt b/sw/qa/core/unocore/data/broken-embedded-object.odt
new file mode 100644
index 000000000000..94a0809235e9
--- /dev/null
+++ b/sw/qa/core/unocore/data/broken-embedded-object.odt
Binary files differ
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index a4a39489da60..a0cd7a253cba 100644
--- a/sw/qa/core/unocore/unocore.cxx
+++ b/sw/qa/core/unocore/unocore.cxx
@@ -31,6 +31,7 @@
#include <comphelper/propertyvalue.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <vcl/graphicfilter.hxx>
+#include <vcl/errinf.hxx>
#include <wrtsh.hxx>
#include <ndtxt.hxx>
@@ -108,6 +109,36 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, flyAtParaAnchor)
xText->insertTextContent(xAnchor, xFieldmark, false);
}
+/// Fails the test if an error popup would be presented.
+static void BasicDisplayErrorHandler(const OUString& /*rErr*/, const OUString& /*rAction*/)
+{
+ CPPUNIT_ASSERT(false);
+}
+
+CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testBrokenEmbeddedObject)
+{
+ // Given a document with a broken embedded object (the XML markup is not well-formed):
+ load(DATA_DIRECTORY, "broken-embedded-object.odt");
+ uno::Reference<text::XTextEmbeddedObjectsSupplier> xSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xObjects(xSupplier->getEmbeddedObjects(),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xObject(xObjects->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<lang::XServiceInfo> xEmbeddedObject;
+ // Get the property first, which initializes Draw, which would overwrite our error handler.
+ xObject->getPropertyValue("EmbeddedObject") >>= xEmbeddedObject;
+ ErrorRegistry::RegisterDisplay(&BasicDisplayErrorHandler);
+
+ // When trying to load that embedded object:
+ xObject->getPropertyValue("EmbeddedObject") >>= xEmbeddedObject;
+
+ // Then make sure we get a non-empty reference and an error popup it not shown:
+ CPPUNIT_ASSERT(xEmbeddedObject.is());
+ // Without the accompanying fix in place, we got this reference, but first an error popup was
+ // shown to the user.
+ CPPUNIT_ASSERT(
+ xEmbeddedObject->supportsService("com.sun.star.comp.embed.OCommonEmbeddedObject"));
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/core/view/data/update-ole-object-previews.odt b/sw/qa/core/view/data/update-ole-object-previews.odt
new file mode 100644
index 000000000000..e6004afcb8ad
--- /dev/null
+++ b/sw/qa/core/view/data/update-ole-object-previews.odt
Binary files differ
diff --git a/sw/qa/core/view/view.cxx b/sw/qa/core/view/view.cxx
new file mode 100644
index 000000000000..7ba43a5001be
--- /dev/null
+++ b/sw/qa/core/view/view.cxx
@@ -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/.
+ */
+
+#include <swmodeltestbase.hxx>
+
+#include <svtools/embedhlp.hxx>
+#include <vcl/graph.hxx>
+
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <fmtcntnt.hxx>
+#include <frameformats.hxx>
+#include <frmfmt.hxx>
+#include <ndarr.hxx>
+#include <ndindex.hxx>
+#include <ndole.hxx>
+#include <node.hxx>
+#include <wrtsh.hxx>
+
+const OUStringLiteral DATA_DIRECTORY = "/sw/qa/core/view/data/";
+
+namespace
+{
+/// Covers sw/source/core/view/ fixes.
+class Test : public SwModelTestBase
+{
+};
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testUpdateOleObjectPreviews)
+{
+ // Given a document with two embedded objects, both with broken native data:
+ load(DATA_DIRECTORY, "update-ole-object-previews.odt");
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+
+ // When updating the previews of those embedded objects (right after document load, before
+ // painting the OLE objects):
+ pWrtShell->UpdateOleObjectPreviews();
+
+ // Then make sure that the working preview of those objects are not lost:
+ const SwFrameFormats* pFormats = pDoc->GetSpzFrameFormats();
+ CPPUNIT_ASSERT(pFormats);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), pFormats->size());
+ for (size_t i = 0; i < pFormats->size(); ++i)
+ {
+ SwFrameFormat* pFormat = (*pFormats)[i];
+
+ const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx();
+ CPPUNIT_ASSERT(pNodeIndex);
+ SwNode* pNode = pDoc->GetNodes()[pNodeIndex->GetIndex() + 1];
+ SwOLENode* pOleNode = pNode->GetOLENode();
+ CPPUNIT_ASSERT(pOleNode);
+ SwOLEObj& rOleObj = pOleNode->GetOLEObj();
+ svt::EmbeddedObjectRef& rObject = rOleObj.GetObject();
+ // Without the accompanying fix in place, this test would have failed, the update broke the
+ // preview of the second embedded object.
+ CPPUNIT_ASSERT(!rObject.GetGraphic()->IsNone());
+ }
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/qa/extras/htmlexport/data/transparent.png b/sw/qa/extras/htmlexport/data/transparent.png
new file mode 100644
index 000000000000..936980b0a19b
--- /dev/null
+++ b/sw/qa/extras/htmlexport/data/transparent.png
Binary files differ
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 50e001c72890..7e674160c4f8 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -38,6 +38,10 @@
#include <filter/msfilter/rtfutil.hxx>
#include <sot/storage.hxx>
#include <svl/eitem.hxx>
+#include <vcl/dibtools.hxx>
+#include <editeng/brushitem.hxx>
+
+#include <itabenum.hxx>
namespace
{
@@ -129,6 +133,7 @@ bool TestReqIfRtfReader::WriteObjectData(SvStream& rOLE)
struct OLE1Reader
{
sal_uInt32 m_nNativeDataSize;
+ std::vector<char> m_aNativeData;
sal_uInt32 m_nPresentationDataSize;
OLE1Reader(SvStream& rStream);
@@ -150,7 +155,8 @@ OLE1Reader::OLE1Reader(SvStream& rStream)
rStream.SeekRel(nData);
rStream.ReadUInt32(m_nNativeDataSize);
- rStream.SeekRel(m_nNativeDataSize);
+ m_aNativeData.resize(m_nNativeDataSize);
+ rStream.ReadBytes(m_aNativeData.data(), m_aNativeData.size());
rStream.ReadUInt32(nData); // OLEVersion for presentation data
CPPUNIT_ASSERT(rStream.good());
@@ -695,8 +701,7 @@ DECLARE_HTMLEXPORT_TEST(testReqIfTable, "reqif-table.xhtml")
// <div> was missing, so the XHTML fragment wasn't a valid
// xhtml.BlkStruct.class type anymore.
assertXPath(pDoc, "/html/body/div/table/tr/th", 1);
- // The attribute was present to contain "background" and "border", which is
- // ignored in reqif-xhtml.
+ // Make sure that the cell background is not written using CSS.
assertXPathNoAttribute(pDoc, "/html/body/div/table/tr/th", "style");
// The attribute was present, which is not valid in reqif-xhtml.
assertXPathNoAttribute(pDoc, "/html/body/div/table/tr/th", "bgcolor");
@@ -1426,6 +1431,48 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testListHeading)
assertXPathContent(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", "list header");
}
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testPartiallyNumberedList)
+{
+ // Given a document with a list, first para is numbered, second is not:
+ loadURL("private:factory/swriter", nullptr);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->Insert("list header");
+ SwDoc* pDoc = pWrtShell->GetDoc();
+ sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
+ SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->nNode.GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ }
+ pWrtShell->Insert2("numbered");
+ pWrtShell->SplitNode();
+ pWrtShell->Insert2("not numbered");
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->nNode.GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ rTextNode.SetCountedInList(false);
+ }
+
+ // When exporting to ReqIF:
+ ExportToReqif();
+
+ // Then make sure the output is well-formed xhtml:
+ SvMemoryStream aStream;
+ HtmlExportTest::wrapFragment(maTempFile, aStream);
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ CPPUNIT_ASSERT(pXmlDoc);
+ // Without the accompanying fix in place, this test would have failed:
+ // - expected: <li><p>...</p><p>...</p></li>
+ // - actual : <li><p>...</p><p>...</p>
+ // because a <li> without a matching </li> is not well-formed, and the </li> was omitted because
+ // the second para was not numbered.
+ assertXPath(pXmlDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p", 2);
+}
+
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testBlockQuoteNoMargin)
{
// Given a document with some text, para style set to Quotations, no bottom margin:
@@ -1494,6 +1541,223 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifImageToOle)
CPPUNIT_ASSERT(aOle1Reader.m_nPresentationDataSize);
}
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifOleBmpTransparent)
+{
+ // Given a document with a transparent image:
+ loadURL("private:factory/swriter", nullptr);
+ OUString aImageURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "transparent.png";
+ uno::Sequence<beans::PropertyValue> aArgs = {
+ comphelper::makePropertyValue("FileName", aImageURL),
+ };
+ dispatchCommand(mxComponent, ".uno:InsertGraphic", aArgs);
+
+ // When exporting to reqif with ExportImagesAsOLE=true:
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aStoreProperties = {
+ comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+ comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+ comphelper::makePropertyValue("ExportImagesAsOLE", true),
+ };
+ xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+ // Then make sure the transparent pixel turns into white:
+ OUString aRtfUrl = GetOlePath();
+ SvMemoryStream aRtf;
+ HtmlExportTest::wrapRtfFragment(aRtfUrl, aRtf);
+ tools::SvRef<TestReqIfRtfReader> xReader(new TestReqIfRtfReader(aRtf));
+ CPPUNIT_ASSERT(xReader->CallParser() != SvParserState::Error);
+ SvMemoryStream aOle1;
+ CPPUNIT_ASSERT(xReader->WriteObjectData(aOle1));
+ OLE1Reader aOle1Reader(aOle1);
+ SvMemoryStream aBitmapStream(aOle1Reader.m_aNativeData.data(), aOle1Reader.m_aNativeData.size(),
+ StreamMode::READ);
+ Bitmap aBitmap;
+ ReadDIB(aBitmap, aBitmapStream, /*bFileHeader=*/true);
+ Size aBitmapSize = aBitmap.GetSizePixel();
+ BitmapEx aBitmapEx(aBitmap);
+ Color nActualColor
+ = aBitmapEx.GetPixelColor(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: Color: R:255 G:255 B:255 A:0
+ // - Actual : Color: R:0 G:0 B:0 A:0
+ // i.e. the bitmap without an alpha channel was black, not white.
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, nActualColor);
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testNestedBullets)
+{
+ // Given a documented with nested lists:
+ loadURL("private:factory/swriter", nullptr);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->Insert("first");
+ sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
+ SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->nNode.GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ rTextNode.SetAttrListLevel(0);
+ }
+ pWrtShell->SplitNode();
+ pWrtShell->Insert("second");
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->nNode.GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ rTextNode.SetAttrListLevel(1);
+ }
+
+ // When exporting to xhtml:
+ ExportToReqif();
+
+ // Then make sure that there is a <li> between the outer and the inner <ol>:
+ SvMemoryStream aStream;
+ HtmlExportTest::wrapFragment(maTempFile, aStream);
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ CPPUNIT_ASSERT(pDoc);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - XPath '//reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p' not found
+ // i.e. the <li> inside the outer <ol> was missing.
+ assertXPathContent(
+ pXmlDoc, "//reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p",
+ "second");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTableBackground)
+{
+ // Given a document with two tables: first stable has a background, second table has a
+ // background in its first row:
+ loadURL("private:factory/swriter", nullptr);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ SwInsertTableOptions aInsertTableOptions(SwInsertTableFlags::DefaultBorder,
+ /*nRowsToRepeat=*/0);
+ pWrtShell->InsertTable(aInsertTableOptions, /*nRows=*/1, /*nCols=*/1);
+ pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
+ SvxBrushItem aBrush(Color(0xff0000), RES_BACKGROUND);
+ pWrtShell->SetTabBackground(aBrush);
+ pWrtShell->Down(/*bSelect=*/false);
+ pWrtShell->SplitNode();
+ pWrtShell->InsertTable(aInsertTableOptions, /*nRows=*/2, /*nCols=*/1);
+ pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
+ aBrush.SetColor(0x00ff00);
+ pWrtShell->SetRowBackground(aBrush);
+ pWrtShell->Down(/*bSelect=*/false);
+ // Second row has an explicit transparent background.
+ aBrush.SetColor(COL_TRANSPARENT);
+ pWrtShell->SetRowBackground(aBrush);
+
+ // When exporting to reqif-xhtml:
+ ExportToReqif();
+
+ // Then make sure that CSS markup is used, not HTML one:
+ SvMemoryStream aStream;
+ HtmlExportTest::wrapFragment(maTempFile, aStream);
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - XPath '//reqif-xhtml:table[1]' no attribute 'style' exist
+ // i.e. HTML markup was used for the table background color.
+ assertXPath(pXmlDoc, "//reqif-xhtml:table[1]", "style", "background: #ff0000");
+ assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:table[1]", "bgcolor");
+ assertXPath(pXmlDoc, "//reqif-xhtml:table[2]/reqif-xhtml:tr[1]", "style",
+ "background: #00ff00");
+ assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:table[2]/reqif-xhtml:tr[1]", "bgcolor");
+ // Second row has no explicit style, the default is not written.
+ assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:table[2]/reqif-xhtml:tr[2]", "style");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTab)
+{
+ // Given a document with leading tabs:
+ loadURL("private:factory/swriter", nullptr);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->Insert("\t first");
+ pWrtShell->SplitNode();
+ pWrtShell->Insert("\t\t second");
+ pWrtShell->SplitNode();
+ pWrtShell->Insert("thi \t rd");
+
+ // When exporting to HTML, using LeadingTabWidth=2:
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aStoreProperties = {
+ comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+ comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+ comphelper::makePropertyValue("LeadingTabWidth", static_cast<sal_Int32>(2)),
+ };
+ xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+ // Then make sure that leading tabs are replaced with 2 nbsps:
+ SvMemoryStream aStream;
+ HtmlExportTest::wrapFragment(maTempFile, aStream);
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ CPPUNIT_ASSERT(pDoc);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: <nbsp><nbsp><space>first
+ // - Actual : <tab><space>first
+ // i.e. the leading tab was not replaced by 2 nbsps.
+ assertXPathContent(pXmlDoc, "//reqif-xhtml:p[1]", u"\xa0\xa0 first");
+ // Test a leading tab that is not at the start of the paragraph:
+ assertXPathContent(pXmlDoc, "//reqif-xhtml:p[2]", u"\xa0\xa0\xa0\xa0 second");
+ // Test a tab which is not leading:
+ assertXPathContent(pXmlDoc, "//reqif-xhtml:p[3]", u"thi \t rd");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTabHTML)
+{
+ // Given a document with leading tabs:
+ loadURL("private:factory/swriter", nullptr);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->Insert("\t test");
+
+ // When exporting to plain HTML, using LeadingTabWidth=2:
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aStoreProperties = {
+ comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+ comphelper::makePropertyValue("LeadingTabWidth", static_cast<sal_Int32>(2)),
+ };
+ xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+ // Then make sure that leading tabs are replaced with 2 nbsps:
+ htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
+ CPPUNIT_ASSERT(pHtmlDoc);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: <newline><nbsp><nbsp><space>test
+ // - Actual : <newline><tab><space>test
+ // i.e. the leading tab was not replaced by 2 nbsps.
+ assertXPathContent(pHtmlDoc, "/html/body/p", SAL_NEWLINE_STRING u"\xa0\xa0 test");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testSectionDir)
+{
+ // Given a document with a section:
+ loadURL("private:factory/swriter", nullptr);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->Insert("test");
+ pWrtShell->SelAll();
+ SwSectionData aSectionData(SectionType::Content, "mysect");
+ pWrtShell->InsertSection(aSectionData);
+
+ // When exporting to (reqif-)xhtml:
+ ExportToReqif();
+
+ // Then make sure CSS is used to export the text direction of the section:
+ SvMemoryStream aStream;
+ HtmlExportTest::wrapFragment(maTempFile, aStream);
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - XPath '//reqif-xhtml:div[@id='mysect']' no attribute 'style' exist
+ // i.e. the dir="ltr" HTML attribute was used instead.
+ assertXPath(pXmlDoc, "//reqif-xhtml:div[@id='mysect']", "style", "dir: ltr");
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/layout/data/tdf134298.ott b/sw/qa/extras/layout/data/tdf134298.ott
new file mode 100644
index 000000000000..effb595eb328
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf134298.ott
Binary files differ
diff --git a/sw/qa/extras/layout/data/tdf138039.odt b/sw/qa/extras/layout/data/tdf138039.odt
new file mode 100644
index 000000000000..f355fd1349a6
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf138039.odt
Binary files differ
diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx
index 44512e2ce8a2..cb873bbf59b6 100644
--- a/sw/qa/extras/layout/layout.cxx
+++ b/sw/qa/extras/layout/layout.cxx
@@ -4235,6 +4235,51 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf134548)
}
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf138039)
+{
+ createDoc("tdf138039.odt");
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // there are 3 pages
+ assertXPath(pXmlDoc, "/root/page", 3);
+ // table on first page
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt", 0);
+ // paragraph with large fly on second page
+ assertXPath(pXmlDoc, "/root/page[2]/body/tab", 0);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "top", "17915");
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "height",
+ "15819");
+ // paragraph on third page
+ assertXPath(pXmlDoc, "/root/page[3]/body/tab", 0);
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt", 1);
+ assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/anchored", 0);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf134298)
+{
+ createDoc("tdf134298.ott");
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // there are 2 pages
+ assertXPath(pXmlDoc, "/root/page", 2);
+ // table and first para on first page
+ assertXPath(pXmlDoc, "/root/page[1]/body/tab", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored", 0);
+ // paragraph with large fly on second page
+ assertXPath(pXmlDoc, "/root/page[2]/body/tab", 0);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly", 1);
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "top", "17897");
+ assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "height",
+ "15819");
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/uibase/shells/data/ole-preview-update.odt b/sw/qa/uibase/shells/data/ole-preview-update.odt
new file mode 100644
index 000000000000..3fd4d264628e
--- /dev/null
+++ b/sw/qa/uibase/shells/data/ole-preview-update.odt
Binary files differ
diff --git a/sw/qa/uibase/shells/shells.cxx b/sw/qa/uibase/shells/shells.cxx
index fbdf35758156..75f975fabaaa 100644
--- a/sw/qa/uibase/shells/shells.cxx
+++ b/sw/qa/uibase/shells/shells.cxx
@@ -17,6 +17,7 @@
#include <editeng/adjustitem.hxx>
#include <editeng/outlobj.hxx>
#include <editeng/editobj.hxx>
+#include <unotools/ucbstreamhelper.hxx>
#include <IDocumentContentOperations.hxx>
#include <cmdid.h>
@@ -139,6 +140,28 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testOleSavePreviewUpdate)
CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Object 2"));
}
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testOlePreviewUpdate)
+{
+ // Given a document with an embedded Writer object:
+ load(DATA_DIRECTORY, "ole-preview-update.odt");
+
+ // When updating "all" (including OLE previews):
+ dispatchCommand(mxComponent, ".uno:UpdateAll", {});
+
+ // Then make sure the preview is no longer a 0-sized stream:
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeToURL(maTempFile.GetURL(), {});
+ uno::Reference<packages::zip::XZipFileAccess2> xNameAccess
+ = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory),
+ maTempFile.GetURL());
+ uno::Reference<io::XInputStream> xInputStream(
+ xNameAccess->getByName("ObjectReplacements/Object 1"), uno::UNO_QUERY);
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ // Without the accompanying fix in place, this test would have failed, the stream was still
+ // empty.
+ CPPUNIT_ASSERT_GREATER(static_cast<sal_uInt64>(0), pStream->remainingSize());
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/number.cxx b/sw/source/core/doc/number.cxx
index 1de08ae9f761..b812897a9ae1 100644
--- a/sw/source/core/doc/number.cxx
+++ b/sw/source/core/doc/number.cxx
@@ -1099,6 +1099,29 @@ void SwNumRule::SetGrabBagItem(const uno::Any& rVal)
mpGrabBagItem->PutValue(rVal, 0);
}
+bool SwNumRule::HasContinueList() const
+{
+ // In case all text nodes are after each other, then we won't have a later list that wants to
+ // continue us.
+ sal_uLong nIndex(0);
+ for (size_t i = 0; i < maTextNodeList.size(); ++i)
+ {
+ SwTextNode* pNode = maTextNodeList[i];
+ if (i > 0)
+ {
+ if (pNode->GetIndex() != nIndex + 1)
+ {
+ // May have a continue list.
+ return true;
+ }
+ }
+ nIndex = pNode->GetIndex();
+ }
+
+ // Definitely won't have a continue list.
+ return false;
+}
+
namespace numfunc
{
namespace {
diff --git a/sw/source/core/edit/edfcol.cxx b/sw/source/core/edit/edfcol.cxx
index d0154b4886ef..b2299ec42213 100644
--- a/sw/source/core/edit/edfcol.cxx
+++ b/sw/source/core/edit/edfcol.cxx
@@ -402,7 +402,8 @@ std::pair<bool, OUString> lcl_MakeParagraphSignatureFieldText(const SignatureDes
valid = valid
&& aInfo.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
- msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.ouSubject + ", " +
+ assert(aInfo.GetSigningCertificate()); // it was valid
+ msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.GetSigningCertificate()->X509Subject + ", " +
aDescr.msDate;
msg += (!aDescr.msUsage.isEmpty() ? (" (" + aDescr.msUsage + "): ") : OUString(": "));
msg += (valid ? SwResId(STR_VALID) : SwResId(STR_INVALID));
diff --git a/sw/source/core/inc/pagefrm.hxx b/sw/source/core/inc/pagefrm.hxx
index 3c7f29f52ad0..c8101ef3157b 100644
--- a/sw/source/core/inc/pagefrm.hxx
+++ b/sw/source/core/inc/pagefrm.hxx
@@ -438,6 +438,9 @@ SwTextGridItem const* GetGridItem(SwPageFrame const*const);
sal_uInt16 GetGridWidth(SwTextGridItem const&, SwDoc const&);
+namespace sw { bool IsPageFrameEmpty(SwPageFrame const& rPage); }
+
+
#endif // INCLUDED_SW_SOURCE_CORE_INC_PAGEFRM_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
index 7f913c8a02ff..d3dd8b62027d 100644
--- a/sw/source/core/layout/layact.cxx
+++ b/sw/source/core/layout/layact.cxx
@@ -447,15 +447,19 @@ void SwLayAction::InternalAction(OutputDevice* pRenderContext)
sal_uInt16 nPercentPageNum = 0;
while ((!IsInterrupt() && pPage) || (m_nCheckPageNum != USHRT_MAX))
{
- if (!pPage && m_nCheckPageNum != USHRT_MAX)
+ // note: this is the only place that consumes and resets m_nCheckPageNum
+ if ((IsInterrupt() || !pPage) && m_nCheckPageNum != USHRT_MAX)
{
- SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
- while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum)
- pPg = static_cast<SwPageFrame*>(pPg->GetNext());
- if (pPg)
- pPage = pPg;
- if (!pPage)
- break;
+ if (!pPage || m_nCheckPageNum < pPage->GetPhyPageNum())
+ {
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
+ while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum)
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ if (pPg)
+ pPage = pPg;
+ if (!pPage)
+ break;
+ }
SwPageFrame *pTmp = pPage->GetPrev() ?
static_cast<SwPageFrame*>(pPage->GetPrev()) : pPage;
diff --git a/sw/source/core/layout/pagechg.cxx b/sw/source/core/layout/pagechg.cxx
index a3188eb2a5ca..e119ee4dca08 100644
--- a/sw/source/core/layout/pagechg.cxx
+++ b/sw/source/core/layout/pagechg.cxx
@@ -991,11 +991,67 @@ void SwPageFrame::PrepareRegisterChg()
}
}
+namespace sw {
+
+/// check if there's content on the page that requires it to exist
+bool IsPageFrameEmpty(SwPageFrame const& rPage)
+{
+ bool bExistEssentialObjs = ( nullptr != rPage.GetSortedObjs() );
+ if ( bExistEssentialObjs )
+ {
+ // Only because the page has Flys does not mean that it is needed. If all Flys are
+ // attached to generic content it is also superfluous (checking DocBody should be enough)
+ // OD 19.06.2003 #108784# - consider that drawing objects in
+ // header/footer are supported now.
+ bool bOnlySuperfluosObjs = true;
+ const SwSortedObjs &rObjs = *rPage.GetSortedObjs();
+ for ( size_t i = 0; bOnlySuperfluosObjs && i < rObjs.size(); ++i )
+ {
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = rObjs[i];
+ // OD 2004-01-19 #110582# - do not consider hidden objects
+ if ( rPage.GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
+ pAnchoredObj->GetDrawObj()->GetLayer() ) &&
+ !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ bOnlySuperfluosObjs = false;
+ }
+ }
+ bExistEssentialObjs = !bOnlySuperfluosObjs;
+ }
+
+ // OD 19.06.2003 #108784# - optimization: check first, if essential objects
+ // exists.
+ const SwLayoutFrame* pBody = nullptr;
+ if ( bExistEssentialObjs ||
+ rPage.FindFootnoteCont() ||
+ ( nullptr != ( pBody = rPage.FindBodyCont() ) &&
+ ( pBody->ContainsContent() ||
+ // #i47580#
+ // Do not delete page if there's an empty tabframe
+ // left. I think it might be correct to use ContainsAny()
+ // instead of ContainsContent() to cover the empty-table-case,
+ // but I'm not fully sure, since ContainsAny() also returns
+ // SectionFrames. Therefore I prefer to do it the safe way:
+ ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) )
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+} // namespace sw
+
//FIXME: provide missing documentation
/** Check all pages (starting from the given one) if they use the appropriate frame format.
*
* If "wrong" pages are found, try to fix this as simple as possible.
*
+ * Also delete pages that don't have content on them.
+ *
* @param pStart the page from where to start searching
* @param bNotifyFields
* @param ppPrev
@@ -1033,7 +1089,10 @@ void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFra
SwPageFrame *pNextPage = static_cast<SwPageFrame*>(pPage->GetNext());
SwPageDesc *pDesc = pPage->FindPageDesc();
+ /// page is intentionally empty page
bool bIsEmpty = pPage->IsEmptyPage();
+ // false for intentionally empty pages, they need additional check
+ bool isPageFrameEmpty(!bIsEmpty && sw::IsPageFrameEmpty(*pPage));
bool bIsOdd = pPage->OnRightPage();
bool bWantOdd = pPage->WannaRightPage();
bool bFirst = pPage->OnFirstPage();
@@ -1130,6 +1189,7 @@ void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFra
pTmp->Paste( pRoot, pPage );
pTmp->PreparePage( false );
pPage = pTmp;
+ isPageFrameEmpty = false; // don't delete it right away!
}
else if ( pPage->GetPageDesc() != pDesc ) //4.
{
@@ -1173,16 +1233,21 @@ void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFra
}
#endif
}
- if ( bIsEmpty )
+ assert(!bIsEmpty || !isPageFrameEmpty);
+ if (bIsEmpty || isPageFrameEmpty)
{
// It also might be that an empty page is not needed at all.
// However, the algorithm above cannot determine that. It is not needed if the following
// page can live without it. Do obtain that information, we need to dig deeper...
SwPageFrame *pPg = static_cast<SwPageFrame*>(pPage->GetNext());
- if( !pPg || pPage->OnRightPage() == pPg->WannaRightPage() )
+ if (isPageFrameEmpty || !pPg || pPage->OnRightPage() == pPg->WannaRightPage())
{
// The following page can find a FrameFormat or has no successor -> empty page not needed
SwPageFrame *pTmp = static_cast<SwPageFrame*>(pPage->GetNext());
+ if (isPageFrameEmpty && pPage->GetPrev())
+ { // check previous *again* vs. its new next! see "ooo321_stylepagenumber.odt"
+ pTmp = static_cast<SwPageFrame*>(pPage->GetPrev());
+ }
pPage->Cut();
bool bUpdatePrev = false;
if (ppPrev && *ppPrev == pPage)
@@ -1442,44 +1507,7 @@ void SwRootFrame::RemoveSuperfluous()
// Check the corresponding last page if it is empty and stop loop at the last non-empty page.
do
{
- bool bExistEssentialObjs = ( nullptr != pPage->GetSortedObjs() );
- if ( bExistEssentialObjs )
- {
- // Only because the page has Flys does not mean that it is needed. If all Flys are
- // attached to generic content it is also superfluous (checking DocBody should be enough)
- // OD 19.06.2003 #108784# - consider that drawing objects in
- // header/footer are supported now.
- bool bOnlySuperfluosObjs = true;
- SwSortedObjs &rObjs = *pPage->GetSortedObjs();
- for ( size_t i = 0; bOnlySuperfluosObjs && i < rObjs.size(); ++i )
- {
- // #i28701#
- SwAnchoredObject* pAnchoredObj = rObjs[i];
- // OD 2004-01-19 #110582# - do not consider hidden objects
- if ( pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
- pAnchoredObj->GetDrawObj()->GetLayer() ) &&
- !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
- {
- bOnlySuperfluosObjs = false;
- }
- }
- bExistEssentialObjs = !bOnlySuperfluosObjs;
- }
-
- // OD 19.06.2003 #108784# - optimization: check first, if essential objects
- // exists.
- const SwLayoutFrame* pBody = nullptr;
- if ( bExistEssentialObjs ||
- pPage->FindFootnoteCont() ||
- ( nullptr != ( pBody = pPage->FindBodyCont() ) &&
- ( pBody->ContainsContent() ||
- // #i47580#
- // Do not delete page if there's an empty tabframe
- // left. I think it might be correct to use ContainsAny()
- // instead of ContainsContent() to cover the empty-table-case,
- // but I'm not fully sure, since ContainsAny() also returns
- // SectionFrames. Therefore I prefer to do it the safe way:
- ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) )
+ if (!sw::IsPageFrameEmpty(*pPage))
{
if ( pPage->IsFootnotePage() )
{
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index b89b21b9205a..009099250954 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -2706,6 +2706,21 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext)
aNotify.SetInvaKeep();
}
+static bool IsNextOnSamePage(SwPageFrame const& rPage,
+ SwTabFrame const& rTabFrame, SwTextFrame const& rAnchorFrame)
+{
+ for (SwContentFrame const* pContentFrame = rTabFrame.FindNextCnt();
+ pContentFrame && pContentFrame->FindPageFrame() == &rPage;
+ pContentFrame = pContentFrame->FindNextCnt())
+ {
+ if (pContentFrame == &rAnchorFrame)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
/// Calculate the offsets arising because of FlyFrames
bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
long& rLeftOffset,
@@ -2835,10 +2850,25 @@ bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
if (bShiftDown)
{
+ // possible cases:
+ // both in body
+ // both in same fly
+ // any comb. of body, footnote, header/footer
+ // to keep it safe, check only in doc body vs page margin for now
long nBottom = aRectFnSet.GetBottom(aFlyRect);
- if( aRectFnSet.YDiff( nPrtPos, nBottom ) < 0 )
- nPrtPos = nBottom;
- bInvalidatePrtArea = true;
+ // tdf#138039 don't grow beyond the page body
+ // if the fly is anchored below the table; the fly
+ // must move with its anchor frame to the next page
+ SwRectFnSet fnPage(pPage);
+ if (!IsInDocBody() // TODO
+ || fnPage.YDiff(fnPage.GetBottom(aFlyRect), fnPage.GetPrtBottom(*pPage)) <= 0
+ || !IsNextOnSamePage(*pPage, *this,
+ *static_cast<SwTextFrame*>(pFly->GetAnchorFrameContainingAnchPos())))
+ {
+ if (aRectFnSet.YDiff( nPrtPos, nBottom ) < 0)
+ nPrtPos = nBottom;
+ bInvalidatePrtArea = true;
+ }
}
if ( (css::text::WrapTextMode_RIGHT == rSur.GetSurround() ||
css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&&
diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx
index 57db1547a1c6..11d20243baf4 100644
--- a/sw/source/core/layout/wsfrm.cxx
+++ b/sw/source/core/layout/wsfrm.cxx
@@ -56,6 +56,7 @@
#include <sortedobjs.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
+#include <layact.hxx>
#include <ndtxt.hxx>
#include <swtable.hxx>
@@ -1200,6 +1201,21 @@ void SwContentFrame::Cut()
if ( pRoot )
{
pRoot->SetSuperfluous();
+ // RemoveSuperfluous can only remove empty pages at the end;
+ // find if there are pages without content following pPage
+ // and if so request a call to CheckPageDescs()
+ SwPageFrame const* pNext(pPage);
+ if (pRoot->GetCurrShell()->Imp()->IsAction())
+ {
+ while ((pNext = static_cast<SwPageFrame const*>(pNext->GetNext())))
+ {
+ if (!sw::IsPageFrameEmpty(*pNext) && !pNext->IsFootnotePage())
+ {
+ pRoot->GetCurrShell()->Imp()->GetLayAction().SetCheckPageNum(pPage->GetPhyPageNum());
+ break;
+ }
+ }
+ }
GetUpper()->SetCompletePaint();
GetUpper()->InvalidatePage( pPage );
}
diff --git a/sw/source/core/ole/ndole.cxx b/sw/source/core/ole/ndole.cxx
index 1927872ed0d9..f6ca51279a29 100644
--- a/sw/source/core/ole/ndole.cxx
+++ b/sw/source/core/ole/ndole.cxx
@@ -54,6 +54,7 @@
#include <atomic>
#include <deque>
#include <libxml/xmlwriter.h>
+#include <flyfrm.hxx>
using namespace utl;
using namespace com::sun::star::uno;
@@ -909,6 +910,36 @@ bool SwOLEObj::IsOleRef() const
return m_xOLERef.is();
}
+IMPL_LINK_NOARG(SwOLEObj, IsProtectedHdl, LinkParamNone*, bool) { return IsProtected(); }
+
+bool SwOLEObj::IsProtected() const
+{
+ if (!m_pOLENode)
+ {
+ return false;
+ }
+
+ SwFrame* pFrame = m_pOLENode->getLayoutFrame(nullptr);
+ if (!pFrame)
+ {
+ return false;
+ }
+ SwFrame* pUpper = pFrame->GetUpper();
+ if (!pUpper || !pUpper->IsFlyFrame())
+ {
+ return false;
+ }
+
+ auto pFlyFrame = static_cast<SwFlyFrame*>(pUpper);
+ const SwFrame* pAnchor = pFlyFrame->GetAnchorFrame();
+ if (!pAnchor)
+ {
+ return false;
+ }
+
+ return pAnchor->IsProtected();
+}
+
uno::Reference < embed::XEmbeddedObject > const & SwOLEObj::GetOleRef()
{
if( !m_xOLERef.is() )
@@ -942,6 +973,7 @@ uno::Reference < embed::XEmbeddedObject > const & SwOLEObj::GetOleRef()
}
if (xObj.is())
{
+ m_xOLERef.SetIsProtectedHdl(LINK(this, SwOLEObj, IsProtectedHdl));
m_xOLERef.Assign( xObj, m_xOLERef.GetViewAspect() );
m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), m_aName );
m_xListener = new SwOLEListener_Impl( this );
diff --git a/sw/source/core/unocore/unoframe.cxx b/sw/source/core/unocore/unoframe.cxx
index a04ac6908b92..58801e2fb971 100644
--- a/sw/source/core/unocore/unoframe.cxx
+++ b/sw/source/core/unocore/unoframe.cxx
@@ -112,6 +112,7 @@
#include <comphelper/servicehelper.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <sal/log.hxx>
+#include <vcl/errinf.hxx>
#include <svx/unobrushitemhelper.hxx>
#include <svx/xbtmpit.hxx>
@@ -1958,9 +1959,28 @@ void SwXFrame::setPropertyValue(const OUString& rPropertyName, const ::uno::Any&
throw uno::RuntimeException();
}
+namespace
+{
+/// Redirect error popups to developer warnings for the duration of the UNO API call.
+class DisplayLockGuard
+{
+ bool m_bLock;
+
+public:
+ DisplayLockGuard()
+ {
+ m_bLock = ErrorRegistry::GetLock();
+ ErrorRegistry::SetLock(true);
+ }
+
+ ~DisplayLockGuard() { ErrorRegistry::SetLock(m_bLock); }
+};
+}
+
uno::Any SwXFrame::getPropertyValue(const OUString& rPropertyName)
{
SolarMutexGuard aGuard;
+ DisplayLockGuard aDisplayGuard;
uno::Any aAny;
SwFrameFormat* pFormat = GetFrameFormat();
const SfxItemPropertySimpleEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName);
diff --git a/sw/source/core/unocore/unoparagraph.cxx b/sw/source/core/unocore/unoparagraph.cxx
index 3fef41218cad..45fbd9c31e41 100644
--- a/sw/source/core/unocore/unoparagraph.cxx
+++ b/sw/source/core/unocore/unoparagraph.cxx
@@ -930,6 +930,22 @@ static beans::PropertyState lcl_SwXParagraph_getPropertyState(
bDone = true;
break;
}
+ case FN_UNO_LIST_ID:
+ {
+ if (*ppSet)
+ {
+ if ((*ppSet)->GetItemState(RES_PARATR_LIST_ID, false) == SfxItemState::SET)
+ {
+ SwNumRule* pNumRule = rTextNode.GetNumRule();
+ if (!pNumRule || pNumRule->HasContinueList())
+ {
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+ bDone = true;
+ }
+ break;
+ }
case FN_UNO_ANCHOR_TYPES:
{
bDone = true;
diff --git a/sw/source/core/view/viewsh.cxx b/sw/source/core/view/viewsh.cxx
index c5d20fe38d88..e30fc909eaad 100644
--- a/sw/source/core/view/viewsh.cxx
+++ b/sw/source/core/view/viewsh.cxx
@@ -81,6 +81,9 @@
#include <vcl/sysdata.hxx>
#endif
+#include <frameformats.hxx>
+#include <fmtcntnt.hxx>
+
bool SwViewShell::mbLstAct = false;
ShellResource *SwViewShell::mpShellRes = nullptr;
vcl::DeleteOnDeinit<std::shared_ptr<weld::Window>> SwViewShell::mpCareDialog(new std::shared_ptr<weld::Window>);
@@ -655,6 +658,44 @@ void SwViewShell::UpdateFields(bool bCloseDB)
EndAction();
}
+void SwViewShell::UpdateOleObjectPreviews()
+{
+ SwDoc* pDoc = GetDoc();
+ const SwFrameFormats* const pFormats = pDoc->GetSpzFrameFormats();
+ if (pFormats->empty())
+ {
+ return;
+ }
+
+ for (size_t i = 0; i < pFormats->size(); ++i)
+ {
+ SwFrameFormat* pFormat = (*pFormats)[i];
+ if (pFormat->Which() != RES_FLYFRMFMT)
+ {
+ continue;
+ }
+
+ const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx();
+ if (!pNodeIndex || !pNodeIndex->GetNodes().IsDocNodes())
+ {
+ continue;
+ }
+
+ SwNode* pNode = pDoc->GetNodes()[pNodeIndex->GetIndex() + 1];
+ SwOLENode* pOleNode = pNode->GetOLENode();
+ if (!pOleNode)
+ {
+ continue;
+ }
+
+ SwOLEObj& rOleObj = pOleNode->GetOLEObj();
+ svt::EmbeddedObjectRef& rObject = rOleObj.GetObject();
+ rObject.UpdateReplacement();
+ // Trigger the repaint.
+ pOleNode->SetChanged();
+ }
+}
+
/** update all charts for which any table exists */
void SwViewShell::UpdateAllCharts()
{
diff --git a/sw/source/filter/html/css1atr.cxx b/sw/source/filter/html/css1atr.cxx
index 9d773603a1ab..8a0bc881e1c8 100644
--- a/sw/source/filter/html/css1atr.cxx
+++ b/sw/source/filter/html/css1atr.cxx
@@ -97,14 +97,6 @@ using editeng::SvxBorderLine;
namespace {
-enum class Css1Background {
- Attr = 1,
- Page = 2,
- Table = 3,
- Fly = 4,
- Section = 5
-};
-
enum class Css1FrameSize {
NONE = 0x00,
Width = 0x01,
@@ -148,7 +140,7 @@ static Writer& OutCSS1_SvxULSpace_SvxLRSpace( Writer& rWrt,
static Writer& OutCSS1_SvxULSpace_SvxLRSpace( Writer& rWrt,
const SfxItemSet& rItemSet );
static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt,
- Css1Background nMode,
+ sw::Css1Background nMode,
const OUString *pGraphicName );
static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt );
static Writer& OutCSS1_SwFormatFrameSize( Writer& rWrt, const SfxPoolItem& rHt,
@@ -185,11 +177,24 @@ OString lclConvToHex(sal_uInt16 nHex)
}
}
-bool IgnorePropertyForReqIF(bool bReqIF, const OString& rProperty, const OString& rValue)
+bool IgnorePropertyForReqIF(bool bReqIF, const OString& rProperty, const OString& rValue,
+ std::optional<sw::Css1Background> oMode)
{
if (!bReqIF)
return false;
+ if (oMode.has_value() && *oMode != sw::Css1Background::TableCell)
+ {
+ // Table or row.
+ if (rProperty == sCSS1_P_background && rValue == "transparent")
+ {
+ // This is the default already.
+ return true;
+ }
+
+ return false;
+ }
+
// Only allow these two keys, nothing else in ReqIF mode.
if (rProperty == sCSS1_P_text_decoration)
{
@@ -243,9 +248,15 @@ public:
void SwHTMLWriter::OutCSS1_Property( const char *pProp,
const char *pVal,
- const OUString *pSVal )
+ const OUString *pSVal,
+ std::optional<sw::Css1Background> oMode )
{
- if (IgnorePropertyForReqIF(mbReqIF, pProp, pVal))
+ OString aPropertyValue(pVal);
+ if (aPropertyValue.isEmpty() && pSVal)
+ {
+ aPropertyValue = pSVal->toUtf8();
+ }
+ if (IgnorePropertyForReqIF(mbReqIF, pProp, aPropertyValue, oMode))
return;
OStringBuffer sOut;
@@ -1811,7 +1822,7 @@ Writer& OutCSS1_BodyTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet )
&pItem ) )
{
OUString rEmbeddedGraphicName;
- OutCSS1_SvxBrush( rWrt, *pItem, Css1Background::Page, &rEmbeddedGraphicName );
+ OutCSS1_SvxBrush( rWrt, *pItem, sw::Css1Background::Page, &rEmbeddedGraphicName );
}
if( SfxItemState::SET == rItemSet.GetItemState( RES_BOX, false,
@@ -1841,7 +1852,6 @@ Writer& OutCSS1_ParaTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet )
return rWrt;
}
-// Wrapper for Table background
Writer& OutCSS1_TableBGStyleOpt( Writer& rWrt, const SfxPoolItem& rHt )
{
SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
@@ -1849,7 +1859,7 @@ Writer& OutCSS1_TableBGStyleOpt( Writer& rWrt, const SfxPoolItem& rHt )
SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_STYLE_OPT_ON |
CSS1_OUTMODE_ENCODE|
CSS1_OUTMODE_TABLEBOX, nullptr );
- OutCSS1_SvxBrush( rWrt, rHt, Css1Background::Table, nullptr );
+ OutCSS1_SvxBrush( rWrt, rHt, sw::Css1Background::TableRow, nullptr );
if( !rHTMLWrt.m_bFirstCSS1Property )
rWrt.Strm().WriteChar( '\"' );
@@ -2097,7 +2107,7 @@ void SwHTMLWriter::OutCSS1_TableFrameFormatOptions( const SwFrameFormat& rFrameF
const SfxPoolItem *pItem;
const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) )
- OutCSS1_SvxBrush( *this, *pItem, Css1Background::Table, nullptr );
+ OutCSS1_SvxBrush( *this, *pItem, sw::Css1Background::Table, nullptr );
if( IsHTMLMode( HTMLMODE_PRINT_EXT ) )
OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( *this, rItemSet, false );
@@ -2109,10 +2119,12 @@ void SwHTMLWriter::OutCSS1_TableFrameFormatOptions( const SwFrameFormat& rFrameF
Strm().WriteChar( '\"' );
}
-void SwHTMLWriter::OutCSS1_TableCellBorderHack(SwFrameFormat const& rFrameFormat)
+void SwHTMLWriter::OutCSS1_TableCellBordersAndBG(SwFrameFormat const& rFrameFormat, const SvxBrushItem *pBrushItem)
{
SwCSS1OutMode const aMode( *this,
CSS1_OUTMODE_STYLE_OPT_ON|CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_TABLEBOX, nullptr );
+ if (pBrushItem)
+ OutCSS1_SvxBrush(*this, *pBrushItem, sw::Css1Background::TableCell, nullptr);
OutCSS1_SvxBox(*this, rFrameFormat.GetBox());
if (!m_bFirstCSS1Property)
{
@@ -2129,7 +2141,18 @@ void SwHTMLWriter::OutCSS1_SectionFormatOptions( const SwFrameFormat& rFrameForm
const SfxPoolItem *pItem;
const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) )
- OutCSS1_SvxBrush( *this, *pItem, Css1Background::Section, nullptr );
+ OutCSS1_SvxBrush( *this, *pItem, sw::Css1Background::Section, nullptr );
+
+ if (mbXHTML)
+ {
+ SvxFrameDirection nDir = GetHTMLDirection(rFrameFormat.GetAttrSet());
+ OString sConvertedDirection = convertDirection(nDir);
+ if (!sConvertedDirection.isEmpty())
+ {
+ OutCSS1_Property(sCSS1_P_dir, sConvertedDirection.getStr(), nullptr,
+ sw::Css1Background::Section);
+ }
+ }
if (pCol)
{
@@ -2151,7 +2174,7 @@ static bool OutCSS1_FrameFormatBrush( SwHTMLWriter& rWrt,
!rBrushItem.GetGraphicLink().isEmpty() ||
0 != rBrushItem.GetGraphicPos() )
{
- OutCSS1_SvxBrush( rWrt, rBrushItem, Css1Background::Fly, nullptr );
+ OutCSS1_SvxBrush( rWrt, rBrushItem, sw::Css1Background::Fly, nullptr );
bWritten = true;
}
return bWritten;
@@ -3128,12 +3151,12 @@ static Writer& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( Writer& rWrt,
// Wrapper for OutCSS1_SfxItemSet etc.
static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt )
{
- OutCSS1_SvxBrush( rWrt, rHt, Css1Background::Attr, nullptr );
+ OutCSS1_SvxBrush( rWrt, rHt, sw::Css1Background::Attr, nullptr );
return rWrt;
}
static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt,
- Css1Background nMode,
+ sw::Css1Background nMode,
const OUString* pGraphicName)
{
SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
@@ -3150,7 +3173,7 @@ static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt,
OUString aLink = pGraphicName ? *pGraphicName
: static_cast<const SvxBrushItem &>(rHt).GetGraphicLink();
SvxGraphicPosition ePos = static_cast<const SvxBrushItem &>(rHt).GetGraphicPos();
- if( Css1Background::Page == nMode && !rHTMLWrt.mbEmbedImages )
+ if( sw::Css1Background::Page == nMode && !rHTMLWrt.mbEmbedImages )
{
// page style images are exported if not tiled
if( aLink.isEmpty() || GPOS_TILED==ePos )
@@ -3193,7 +3216,7 @@ static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt,
aLink = aGraphicAsLink;
}
// In tables we only export something if there is a Graphic
- if( Css1Background::Table==nMode && !pGrf && !aLink.isEmpty())
+ if( (nMode == sw::Css1Background::Table || nMode == sw::Css1Background::TableRow) && !pGrf && !aLink.isEmpty())
return rWrt;
// if necessary, add the orientation of the Graphic
@@ -3264,7 +3287,7 @@ static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt,
if( !pGrf && aLink.isEmpty() && !bColor )
{
// no color and no Link, but a transparent Brush
- if( bTransparent && Css1Background::Fly != nMode )
+ if( bTransparent && sw::Css1Background::Fly != nMode )
sOut += OStringToOUString(sCSS1_PV_transparent, RTL_TEXTENCODING_ASCII_US);
}
else
@@ -3311,7 +3334,10 @@ static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt,
}
if( !sOut.isEmpty() )
- rHTMLWrt.OutCSS1_Property( sCSS1_P_background, sOut );
+ {
+ rHTMLWrt.OutCSS1_Property(sCSS1_P_background, nullptr, &sOut,
+ nMode);
+ }
return rWrt;
}
diff --git a/sw/source/filter/html/css1kywd.cxx b/sw/source/filter/html/css1kywd.cxx
index f8914dedb274..c0299e28c958 100644
--- a/sw/source/filter/html/css1kywd.cxx
+++ b/sw/source/filter/html/css1kywd.cxx
@@ -197,6 +197,7 @@ const char* const sCSS1_P_height = "height";
const char* const sCSS1_P_float = "float";
const char* const sCSS1_P_column_count = "column-count";
+const char* const sCSS1_P_dir = "dir";
// Strings for positioning
diff --git a/sw/source/filter/html/css1kywd.hxx b/sw/source/filter/html/css1kywd.hxx
index 7c0e326fd86a..c4609c5659b1 100644
--- a/sw/source/filter/html/css1kywd.hxx
+++ b/sw/source/filter/html/css1kywd.hxx
@@ -200,6 +200,7 @@ extern const char* const sCSS1_P_height;
extern const char* const sCSS1_P_float;
extern const char* const sCSS1_P_column_count;
+extern const char* const sCSS1_P_dir;
// Strings for positioning
diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx
index 46abffd664a9..31a28db4f9b3 100644
--- a/sw/source/filter/html/htmlatr.cxx
+++ b/sw/source/filter/html/htmlatr.cxx
@@ -2358,6 +2358,8 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode )
{
HTMLOutContext aContext( rHTMLWrt.m_eDestEnc );
+ // Tabs are leading till there is a non-tab since the start of the paragraph.
+ bool bLeadingTab = true;
for( ; nStrPos < nEnd; nStrPos++ )
{
// output the frames that are anchored to the current position
@@ -2491,7 +2493,33 @@ Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode )
rHTMLWrt.OutPointFieldmarks(aMarkPos);
}
else
- HTMLOutFuncs::Out_Char( rWrt.Strm(), c, aContext, &rHTMLWrt.m_aNonConvertableCharacters );
+ {
+ bool bConsumed = false;
+ if (c == '\t')
+ {
+ if (bLeadingTab && rHTMLWrt.m_nLeadingTabWidth.has_value())
+ {
+ // Consume a tab if it's leading and we know the number of NBSPs to
+ // be used as a replacement.
+ for (sal_Int32 i = 0; i < *rHTMLWrt.m_nLeadingTabWidth; ++i)
+ {
+ rWrt.Strm().WriteCharPtr("&#160;");
+ }
+ bConsumed = true;
+ }
+ }
+ else
+ {
+ // Not a tab -> later tabs are no longer leading.
+ bLeadingTab = false;
+ }
+
+ if (!bConsumed)
+ {
+ HTMLOutFuncs::Out_Char(rWrt.Strm(), c, aContext,
+ &rHTMLWrt.m_aNonConvertableCharacters);
+ }
+ }
// if a paragraph's last character is a hard line break
// then we need to add an extra <br>
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
index e5a3b482bd61..e2e8c7bc7424 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -621,7 +621,8 @@ OString SwHTMLWriter::OutFrameFormatOptions( const SwFrameFormat &rFrameFormat,
if( (nFrameOpts & HtmlFrmOpts::Space) &&
(aTwipSpc.Width() || aTwipSpc.Height()) &&
- Application::GetDefaultDevice() )
+ Application::GetDefaultDevice() &&
+ !mbReqIF )
{
Size aPixelSpc =
Application::GetDefaultDevice()->LogicToPixel( aTwipSpc,
@@ -888,7 +889,8 @@ void SwHTMLWriter::writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameForma
if( (nFrameOptions & HtmlFrmOpts::Space) &&
(aTwipSpc.Width() || aTwipSpc.Height()) &&
- Application::GetDefaultDevice() )
+ Application::GetDefaultDevice() &&
+ !mbReqIF )
{
Size aPixelSpc =
Application::GetDefaultDevice()->LogicToPixel( aTwipSpc,
diff --git a/sw/source/filter/html/htmlnumwriter.cxx b/sw/source/filter/html/htmlnumwriter.cxx
index 84ed431699d8..965f7ccbfd70 100644
--- a/sw/source/filter/html/htmlnumwriter.cxx
+++ b/sw/source/filter/html/htmlnumwriter.cxx
@@ -208,11 +208,15 @@ Writer& OutHTML_NumberBulletListStart( SwHTMLWriter& rWrt,
rWrt.m_aBulletGrfs[i].clear();
OString sOut = "<" + rWrt.GetNamespace();
+ if (rWrt.mbXHTML && (nPrevDepth != 0 || i != 0))
+ {
+ sOut += OOO_STRING_SVTOOLS_HTML_li "><" + rWrt.GetNamespace();
+ }
const SwNumFormat& rNumFormat = rInfo.GetNumRule()->Get( i );
sal_Int16 eType = rNumFormat.GetNumberingType();
if( SVX_NUM_CHAR_SPECIAL == eType )
{
- // ordered list: <OL>
+ // unordered list: <UL>
sOut += OString(OOO_STRING_SVTOOLS_HTML_unorderlist);
// determine the type by the bullet character
@@ -314,24 +318,10 @@ Writer& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt,
bool bSameRule = rNextInfo.GetNumRule() == rInfo.GetNumRule();
bool bListEnd = !bSameRule || rNextInfo.GetDepth() < rInfo.GetDepth() || rNextInfo.IsRestart();
- if (rWrt.mbXHTML)
- {
- if ((bListEnd && rInfo.IsNumbered()) || (!bListEnd && rNextInfo.IsNumbered()))
- {
- HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(),
- rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li, false);
- }
- }
-
- if (!bListEnd)
- {
- return rWrt;
- }
-
+ std::optional<bool> oAtLeastOneNumbered;
if (rWrt.mbXHTML && !rInfo.IsNumbered())
{
- // If the list only consisted of non-numbered text nodes, then don't end the list.
- bool bAtLeastOneNumbered = false;
+ oAtLeastOneNumbered = false;
sal_uLong nPos = rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() - 1;
while (true)
{
@@ -349,13 +339,36 @@ Writer& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt,
if (pTextNode->IsNumbered())
{
- bAtLeastOneNumbered = true;
+ oAtLeastOneNumbered = true;
break;
}
--nPos;
}
+ }
+
+ if (rWrt.mbXHTML)
+ {
+ // The list is numbered if the previous text node is numbered or any other previous text
+ // node is numbered.
+ bool bPrevIsNumbered = rInfo.IsNumbered() || *oAtLeastOneNumbered;
+ // XHTML </li> for the list item content, if there is an open <li>.
+ if ((bListEnd && bPrevIsNumbered) || (!bListEnd && rNextInfo.IsNumbered()))
+ {
+ HTMLOutFuncs::Out_AsciiTag(
+ rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li,
+ false);
+ }
+ }
- if (!bAtLeastOneNumbered)
+ if (!bListEnd)
+ {
+ return rWrt;
+ }
+
+ if (rWrt.mbXHTML && !rInfo.IsNumbered())
+ {
+ // If the list only consisted of non-numbered text nodes, then don't end the list.
+ if (!*oAtLeastOneNumbered)
{
return rWrt;
}
@@ -381,6 +394,12 @@ Writer& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt,
else
aTag = OOO_STRING_SVTOOLS_HTML_orderlist;
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + aTag, false );
+ if (rWrt.mbXHTML && (nNextDepth != 0 || i != 1))
+ {
+ HTMLOutFuncs::Out_AsciiTag(
+ rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li,
+ /*bOn=*/false);
+ }
rWrt.m_bLFPossible = true;
}
diff --git a/sw/source/filter/html/htmlplug.cxx b/sw/source/filter/html/htmlplug.cxx
index 5fd6b3e54d7b..169eaae84637 100644
--- a/sw/source/filter/html/htmlplug.cxx
+++ b/sw/source/filter/html/htmlplug.cxx
@@ -1650,6 +1650,9 @@ Writer& OutHTML_FrameFormatOLENodeGrf( Writer& rWrt, const SwFrameFormat& rFrame
if( nErr ) // error, don't write anything
{
rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD;
+ if (bObjectOpened) // Still at least close the tag.
+ rWrt.Strm().WriteOString("</" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object
+ ">");
return rWrt;
}
aGraphicURL = URIHelper::SmartRel2Abs(
diff --git a/sw/source/filter/html/htmlreqifreader.cxx b/sw/source/filter/html/htmlreqifreader.cxx
index 8a6101fa93ee..a091183b9f69 100644
--- a/sw/source/filter/html/htmlreqifreader.cxx
+++ b/sw/source/filter/html/htmlreqifreader.cxx
@@ -545,7 +545,13 @@ bool WrapGraphicInRtf(const Graphic& rGraphic, const SwFrameFormat& rFormat, SvS
// NativeDataSize
SvMemoryStream aNativeData;
- if (GraphicConverter::Export(aNativeData, rGraphic, ConvertDataFormat::BMP) != ERRCODE_NONE)
+
+ // Set white background for the semi-transparent pixels.
+ BitmapEx aBitmapEx = rGraphic.GetBitmapEx();
+ Bitmap aBitmap = aBitmapEx.GetBitmap(/*aTransparentReplaceColor=*/COL_WHITE);
+
+ if (GraphicConverter::Export(aNativeData, BitmapEx(aBitmap), ConvertDataFormat::BMP)
+ != ERRCODE_NONE)
{
SAL_WARN("sw.html", "WrapGraphicInRtf: bmp conversion failed");
}
diff --git a/sw/source/filter/html/htmltabw.cxx b/sw/source/filter/html/htmltabw.cxx
index 1febebd9ff34..81a7daf570a0 100644
--- a/sw/source/filter/html/htmltabw.cxx
+++ b/sw/source/filter/html/htmltabw.cxx
@@ -430,11 +430,14 @@ void SwHTMLWrtTable::OutTableCell( SwHTMLWriter& rWrt,
// Avoid non-CSS version in the ReqIF case.
rWrt.OutBackground( pBrushItem, false );
- if( rWrt.m_bCfgOutStyles )
- OutCSS1_TableBGStyleOpt( rWrt, *pBrushItem );
+ if (!rWrt.m_bCfgOutStyles)
+ pBrushItem = nullptr;
}
- rWrt.OutCSS1_TableCellBorderHack(*pBox->GetFrameFormat());
+ // tdf#132739 with rWrt.m_bCfgOutStyles of true bundle the brush item css
+ // properties into the same "style" tag as the borders so there is only one
+ // style tag
+ rWrt.OutCSS1_TableCellBordersAndBG(*pBox->GetFrameFormat(), pBrushItem);
sal_uInt32 nNumFormat = 0;
double nValue = 0.0;
@@ -533,11 +536,14 @@ void SwHTMLWrtTable::OutTableCells( SwHTMLWriter& rWrt,
rWrt.Strm().WriteChar( '<' ).WriteOString( rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow );
if( pBrushItem )
{
- rWrt.OutBackground( pBrushItem, false );
+ if (!rWrt.mbXHTML)
+ {
+ rWrt.OutBackground(pBrushItem, false);
+ }
rWrt.m_bTextAttr = false;
rWrt.m_bOutOpts = true;
- if( rWrt.m_bCfgOutStyles )
+ if (rWrt.m_bCfgOutStyles || rWrt.mbXHTML)
OutCSS1_TableBGStyleOpt( rWrt, *pBrushItem );
}
@@ -666,7 +672,7 @@ void SwHTMLWrtTable::Write( SwHTMLWriter& rWrt, sal_Int16 eAlign,
sOut.append("\"");
}
- if( (nHSpace || nVSpace) && Application::GetDefaultDevice())
+ if( (nHSpace || nVSpace) && Application::GetDefaultDevice() && !rWrt.mbReqIF)
{
Size aPixelSpc =
Application::GetDefaultDevice()->LogicToPixel( Size(nHSpace,nVSpace),
@@ -702,10 +708,15 @@ void SwHTMLWrtTable::Write( SwHTMLWriter& rWrt, sal_Int16 eAlign,
// output background
if( pFrameFormat )
{
- rWrt.OutBackground( pFrameFormat->GetAttrSet(), false );
+ if (!rWrt.mbXHTML)
+ {
+ rWrt.OutBackground(pFrameFormat->GetAttrSet(), false);
+ }
- if (rWrt.m_bCfgOutStyles)
+ if (rWrt.m_bCfgOutStyles || rWrt.mbXHTML)
+ {
rWrt.OutCSS1_TableFrameFormatOptions( *pFrameFormat );
+ }
}
sOut.append('>');
diff --git a/sw/source/filter/html/wrthtml.cxx b/sw/source/filter/html/wrthtml.cxx
index 2f83734aee37..b8a48c2918f3 100644
--- a/sw/source/filter/html/wrthtml.cxx
+++ b/sw/source/filter/html/wrthtml.cxx
@@ -187,56 +187,131 @@ void SwHTMLWriter::SetupFilterOptions(SfxMedium& rMedium)
return;
const SfxPoolItem* pItem;
- if (pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) != SfxItemState::SET)
- return;
-
-
- const OUString sFilterOptions = static_cast<const SfxStringItem*>(pItem)->GetValue();
- SetupFilterOptions(sFilterOptions);
-
- comphelper::SequenceAsHashMap aStoreMap(rMedium.GetArgs());
- auto it = aStoreMap.find("RTFOLEMimeType");
- if (it == aStoreMap.end())
+ if (pSet->GetItemState(SID_FILE_FILTEROPTIONS, true, &pItem) == SfxItemState::SET)
{
- return;
+ const OUString sFilterOptions = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ SetupFilterOptions(sFilterOptions);
}
- it->second >>= m_aRTFOLEMimeType;
+ SetupFilterFromPropertyValues(rMedium.GetArgs());
}
void SwHTMLWriter::SetupFilterOptions(const OUString& rFilterOptions)
{
- if (rFilterOptions == "SkipImages")
+ comphelper::SequenceAsHashMap aStoreMap;
+ if (rFilterOptions.indexOf("SkipImages") >= 0)
{
- mbSkipImages = true;
+ aStoreMap["SkipImages"] <<= true;
}
- else if (rFilterOptions == "SkipHeaderFooter")
+ else if (rFilterOptions.indexOf("SkipHeaderFooter") >= 0)
{
- mbSkipHeaderFooter = true;
+ aStoreMap["SkipHeaderFooter"] <<= true;
}
- else if (rFilterOptions == "EmbedImages")
+ else if (rFilterOptions.indexOf("EmbedImages") >= 0)
{
- mbEmbedImages = true;
+ aStoreMap["EmbedImages"] <<= true;
}
- const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(rFilterOptions);
+ // this option can be "on" together with any of above
+ if (rFilterOptions.indexOf("NoLineLimit") >= 0)
+ {
+ aStoreMap["NoLineLimit"] <<= true;
+ }
+
+ const uno::Sequence<OUString> aOptionSeq
+ = comphelper::string::convertCommaSeparated(rFilterOptions);
const OUString aXhtmlNsKey("xhtmlns=");
for (const auto& rOption : aOptionSeq)
{
if (rOption == "XHTML")
- mbXHTML = true;
+ {
+ aStoreMap["XHTML"] <<= true;
+ }
else if (rOption.startsWith(aXhtmlNsKey))
{
- maNamespace = rOption.copy(aXhtmlNsKey.getLength()).toUtf8();
- if (maNamespace == "reqif-xhtml")
- {
- mbReqIF = true;
- // XHTML is always just a fragment inside ReqIF.
- mbSkipHeaderFooter = true;
- }
- // XHTML namespace implies XHTML.
- mbXHTML = true;
+ aStoreMap["XhtmlNs"] <<= rOption.copy(aXhtmlNsKey.getLength());
+ }
+ }
+
+ SetupFilterFromPropertyValues(aStoreMap.getAsConstPropertyValueList());
+}
+
+void SwHTMLWriter::SetupFilterFromPropertyValues(
+ const css::uno::Sequence<css::beans::PropertyValue>& rPropertyValues)
+{
+ comphelper::SequenceAsHashMap aStoreMap(rPropertyValues);
+ auto it = aStoreMap.find("RTFOLEMimeType");
+ if (it != aStoreMap.end())
+ {
+ it->second >>= m_aRTFOLEMimeType;
+ }
+
+ it = aStoreMap.find("SkipImages");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ mbSkipImages = bVal;
+ }
+
+ it = aStoreMap.find("SkipHeaderFooter");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ mbSkipHeaderFooter = bVal;
+ }
+
+ it = aStoreMap.find("EmbedImages");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ mbEmbedImages = bVal;
+ }
+
+ it = aStoreMap.find("NoLineLimit");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ if (bVal)
+ {
+ m_nWhishLineLen = -1;
+ }
+ }
+
+ it = aStoreMap.find("XHTML");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ mbXHTML = bVal;
+ }
+
+ it = aStoreMap.find("XhtmlNs");
+ if (it != aStoreMap.end())
+ {
+ OUString aVal;
+ it->second >>= aVal;
+
+ maNamespace = aVal.toUtf8();
+ if (maNamespace == "reqif-xhtml")
+ {
+ mbReqIF = true;
+ // XHTML is always just a fragment inside ReqIF.
+ mbSkipHeaderFooter = true;
}
+ // XHTML namespace implies XHTML.
+ mbXHTML = true;
+ }
+
+ it = aStoreMap.find("LeadingTabWidth");
+ if (it != aStoreMap.end())
+ {
+ sal_Int32 nVal{};
+ it->second >>= nVal;
+ m_nLeadingTabWidth.emplace(nVal);
}
}
@@ -615,7 +690,7 @@ static void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt,
rHTMLWrt.OutNewLine();
OStringBuffer sOut;
- sOut.append('<').append(OOO_STRING_SVTOOLS_HTML_division);
+ sOut.append("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division);
const OUString& rName = rSection.GetSectionName();
if( !rName.isEmpty() && !bContinued )
@@ -626,9 +701,12 @@ static void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt,
sOut.append('\"');
}
- SvxFrameDirection nDir = rHTMLWrt.GetHTMLDirection( rFormat.GetAttrSet() );
rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() );
- rHTMLWrt.OutDirection( nDir );
+ if (!rHTMLWrt.mbXHTML)
+ {
+ SvxFrameDirection nDir = rHTMLWrt.GetHTMLDirection(rFormat.GetAttrSet());
+ rHTMLWrt.OutDirection(nDir);
+ }
if( SectionType::FileLink == rSection.GetType() )
{
diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx
index ffc416f22662..592a403c57de 100644
--- a/sw/source/filter/html/wrthtml.hxx
+++ b/sw/source/filter/html/wrthtml.hxx
@@ -253,6 +253,20 @@ typedef std::set<std::unique_ptr<SwHTMLFormatInfo>,
class IDocumentStylePoolAccess;
+namespace sw
+{
+enum class Css1Background
+{
+ Attr = 1,
+ Page = 2,
+ Table = 3,
+ Fly = 4,
+ Section = 5,
+ TableRow = 6,
+ TableCell = 7,
+};
+}
+
class SW_DLLPUBLIC SwHTMLWriter : public Writer
{
std::unique_ptr<SwHTMLPosFlyFrames> m_pHTMLPosFlyFrames;
@@ -274,6 +288,8 @@ class SW_DLLPUBLIC SwHTMLWriter : public Writer
protected:
ErrCode WriteStream() override;
void SetupFilterOptions(SfxMedium& rMedium) override;
+ void SetupFilterFromPropertyValues(
+ const css::uno::Sequence<css::beans::PropertyValue>& rPropertyValues);
public:
std::vector<OUString> m_aImgMapNames; // written image maps
@@ -407,6 +423,9 @@ public:
OUString m_aRTFOLEMimeType;
+ /// If set, replace leading tabs with this many non-breaking spaces.
+ std::optional<sal_Int32> m_nLeadingTabWidth;
+
/// Construct an instance of SwHTMLWriter and optionally give it
/// the filter options directly, which can also be set via SetupFilterOptions().
explicit SwHTMLWriter( const OUString& rBaseURL, const OUString& rFilterOptions = "" );
@@ -457,7 +476,7 @@ public:
const OString& rVal );
inline void OutCSS1_Property( const char *pProp, const OUString& rVal );
void OutCSS1_Property( const char *pProp, const char *pVal,
- const OUString *pSVal );
+ const OUString *pSVal, std::optional<sw::Css1Background> oBackground = std::nullopt );
void OutCSS1_UnitProperty( const char *pProp, long nVal );
void OutCSS1_PixelProperty( const char *pProp, long nVal, bool bVert );
void OutCSS1_SfxItemSet( const SfxItemSet& rItemSet, bool bDeep=true );
@@ -483,8 +502,12 @@ public:
void writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameFormat& rFrameFormat, const OUString& rAltText, HtmlFrmOpts nFrameOpts);
+ /// Writes the formatting for tables.
void OutCSS1_TableFrameFormatOptions( const SwFrameFormat& rFrameFormat );
- void OutCSS1_TableCellBorderHack(const SwFrameFormat& rFrameFormat);
+
+ /// Writes the borders and background for table cells.
+ void OutCSS1_TableCellBordersAndBG(const SwFrameFormat& rFrameFormat, const SvxBrushItem *pBrushItem);
+
void OutCSS1_SectionFormatOptions( const SwFrameFormat& rFrameFormat, const SwFormatCol *pCol );
void OutCSS1_FrameFormatOptions( const SwFrameFormat& rFrameFormat, HtmlFrmOpts nFrameOpts,
const SdrObject *pSdrObj=nullptr,
@@ -684,7 +707,9 @@ Writer& OutCSS1_ParaTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet );
Writer& OutCSS1_HintSpanTag( Writer& rWrt, const SfxPoolItem& rHt );
Writer& OutCSS1_HintStyleOpt( Writer& rWrt, const SfxPoolItem& rHt );
+/// Writes the background of table rows.
Writer& OutCSS1_TableBGStyleOpt( Writer& rWrt, const SfxPoolItem& rHt );
+
Writer& OutCSS1_NumberBulletListStyleOpt( Writer& rWrt, const SwNumRule& rNumRule,
sal_uInt8 nLevel );
@@ -698,7 +723,8 @@ Writer& OutCSS1_SvxBox( Writer& rWrt, const SfxPoolItem& rHt );
OString GetCSS1_Color(const Color& rColor);
/// Determines if rProperty with a given rValue has to be suppressed due to ReqIF mode.
-bool IgnorePropertyForReqIF(bool bReqIF, const OString& rProperty, const OString& rValue);
+bool IgnorePropertyForReqIF(bool bReqIF, const OString& rProperty, const OString& rValue,
+ std::optional<sw::Css1Background> oBackground = std::nullopt);
#endif // INCLUDED_SW_SOURCE_FILTER_HTML_WRTHTML_HXX
diff --git a/sw/source/filter/xml/xmlfonte.cxx b/sw/source/filter/xml/xmlfonte.cxx
index 9b90f94fd419..79207700c204 100644
--- a/sw/source/filter/xml/xmlfonte.cxx
+++ b/sw/source/filter/xml/xmlfonte.cxx
@@ -46,21 +46,27 @@ SwXMLFontAutoStylePool_Impl::SwXMLFontAutoStylePool_Impl(SwXMLExport& _rExport,
RES_CHRATR_CTL_FONT };
const SfxItemPool& rPool = _rExport.getDoc()->GetAttrPool();
+ std::vector<const SvxFontItem *> aFonts;
for(sal_uInt16 nWhichId : aWhichIds)
{
const SvxFontItem& rFont =
static_cast<const SvxFontItem&>(rPool.GetDefaultItem( nWhichId ));
- Add( rFont.GetFamilyName(), rFont.GetStyleName(),
- rFont.GetFamily(), rFont.GetPitch(),
- rFont.GetCharSet() );
+ aFonts.push_back(&rFont);
for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nWhichId))
{
auto pFont = static_cast<const SvxFontItem *>(pItem);
- Add( pFont->GetFamilyName(), pFont->GetStyleName(),
- pFont->GetFamily(), pFont->GetPitch(),
- pFont->GetCharSet() );
+ aFonts.push_back(pFont);
}
}
+
+ std::sort(aFonts.begin(), aFonts.end(),
+ [](const SvxFontItem* pA, const SvxFontItem* pB) -> bool { return *pA < *pB; });
+ for (const auto& pFont : aFonts)
+ {
+ Add(pFont->GetFamilyName(), pFont->GetStyleName(), pFont->GetFamily(), pFont->GetPitch(),
+ pFont->GetCharSet());
+ }
+
auto const & pDocument = _rExport.getDoc();
m_bEmbedUsedOnly = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_USED_FONTS);
diff --git a/sw/source/uibase/app/docsh.cxx b/sw/source/uibase/app/docsh.cxx
index aeb522be5fc9..71033ee0af69 100644
--- a/sw/source/uibase/app/docsh.cxx
+++ b/sw/source/uibase/app/docsh.cxx
@@ -1134,6 +1134,22 @@ void SwDocShell::GetState(SfxItemSet& rSet)
// OLE-Hdls
IMPL_LINK( SwDocShell, Ole2ModifiedHdl, bool, bNewStatus, void )
{
+ if (m_pWrtShell)
+ {
+ SwOLENode* pOLENode = nullptr;
+ if (!m_pWrtShell->IsTableMode())
+ {
+ pOLENode = m_pWrtShell->GetCursor()->GetNode().GetOLENode();
+ }
+ if (pOLENode)
+ {
+ if (pOLENode->GetOLEObj().IsProtected())
+ {
+ return;
+ }
+ }
+ }
+
if( IsEnableSetModified() )
SetModified( bNewStatus );
}
diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx
index 507919f601d7..001ea389f497 100644
--- a/sw/source/uibase/docvw/edtwin.cxx
+++ b/sw/source/uibase/docvw/edtwin.cxx
@@ -3245,8 +3245,11 @@ void SwEditWin::MouseButtonDown(const MouseEvent& _rMEvt)
case 2:
{
g_bFrameDrag = false;
- if ( !bIsDocReadOnly && rSh.IsInsideSelectedObj(aDocPos) &&
- FlyProtectFlags::NONE == rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) )
+ if (!bIsDocReadOnly && rSh.IsInsideSelectedObj(aDocPos)
+ && (FlyProtectFlags::NONE
+ == rSh.IsSelObjProtected(FlyProtectFlags::Content
+ | FlyProtectFlags::Parent)
+ || rSh.GetSelectionType() == SelectionType::Ole))
{
/* This is no good: on the one hand GetSelectionType is used as flag field
* (take a look into the GetSelectionType method) and on the other hand the
@@ -3266,11 +3269,8 @@ void SwEditWin::MouseButtonDown(const MouseEvent& _rMEvt)
// double click on OLE object --> OLE-InPlace
case SelectionType::Ole:
- if (rSh.IsSelObjProtected(FlyProtectFlags::Content) == FlyProtectFlags::NONE)
- {
- RstMBDownFlags();
- rSh.LaunchOLEObj();
- }
+ RstMBDownFlags();
+ rSh.LaunchOLEObj();
return;
case SelectionType::Frame:
diff --git a/sw/source/uibase/shells/basesh.cxx b/sw/source/uibase/shells/basesh.cxx
index 44d78d6e5821..892f4c8fdfbe 100644
--- a/sw/source/uibase/shells/basesh.cxx
+++ b/sw/source/uibase/shells/basesh.cxx
@@ -734,6 +734,7 @@ void SwBaseShell::Execute(SfxRequest &rReq)
rDis.Execute( FN_UPDATE_TOX );
rDis.Execute( FN_UPDATE_CHARTS );
rSh.CalcLayout();
+ rSh.UpdateOleObjectPreviews();
}
break;
diff --git a/sw/source/uibase/uiview/swcli.cxx b/sw/source/uibase/uiview/swcli.cxx
index 05f5f6ccc95a..4dc86bee5faf 100644
--- a/sw/source/uibase/uiview/swcli.cxx
+++ b/sw/source/uibase/uiview/swcli.cxx
@@ -29,6 +29,8 @@
#include <toolkit/helper/vclunohelper.hxx>
+#include <ndole.hxx>
+
using namespace com::sun::star;
SwOleClient::SwOleClient(SwView *pView, SwEditWin *pWin, const svt::EmbeddedObjectRef& xObj)
@@ -164,4 +166,27 @@ void SwOleClient::FormatChanged()
}
}
+bool SwOleClient::IsProtected() const
+{
+ auto pView = dynamic_cast<SwView*>(GetViewShell());
+ if (!pView)
+ {
+ return false;
+ }
+
+ SwWrtShell& rWrtSh = pView->GetWrtShell();
+ if (rWrtSh.IsTableMode())
+ {
+ return false;
+ }
+
+ SwOLENode* pOLENode = rWrtSh.GetCursor()->GetNode().GetOLENode();
+ if (!pOLENode)
+ {
+ return false;
+ }
+
+ return pOLENode->GetOLEObj().IsProtected();
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/uiview/view.cxx b/sw/source/uibase/uiview/view.cxx
index d7837c800bfb..c21190fecb85 100644
--- a/sw/source/uibase/uiview/view.cxx
+++ b/sw/source/uibase/uiview/view.cxx
@@ -131,7 +131,8 @@ void SwView::ImpSetVerb( SelectionType nSelType )
if ( !GetViewFrame()->GetFrame().IsInPlace() &&
(SelectionType::Ole|SelectionType::Graphic) & nSelType )
{
- if ( m_pWrtShell->IsSelObjProtected(FlyProtectFlags::Content) == FlyProtectFlags::NONE )
+ FlyProtectFlags eProtectFlags = m_pWrtShell->IsSelObjProtected(FlyProtectFlags::Content);
+ if (eProtectFlags == FlyProtectFlags::NONE || nSelType & SelectionType::Ole)
{
if ( nSelType & SelectionType::Ole )
{
diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx b/sw/source/uibase/wrtsh/wrtsh1.cxx
index 0dbb7aaf0796..8d369fa50e40 100644
--- a/sw/source/uibase/wrtsh/wrtsh1.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh1.cxx
@@ -25,6 +25,7 @@
#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
#include <hintids.hxx>
#include <sot/exchange.hxx>
@@ -98,6 +99,7 @@
#include <svtools/embedhlp.hxx>
#include <svx/postattr.hxx>
#include <comphelper/lok.hxx>
+#include <comphelper/propertyvalue.hxx>
#include <memory>
using namespace sw::mark;
@@ -622,6 +624,14 @@ void SwWrtShell::LaunchOLEObj( long nVerb )
if ( !pCli )
pCli = new SwOleClient( &GetView(), &GetView().GetEditWin(), xRef );
+ uno::Reference<lang::XInitialization> xOLEInit(xRef.GetObject(), uno::UNO_QUERY);
+ if (xOLEInit.is())
+ {
+ uno::Sequence<beans::PropertyValue> aArguments
+ = { comphelper::makePropertyValue("ReadOnly", pCli->IsProtected()) };
+ xOLEInit->initialize({ uno::makeAny(aArguments) });
+ }
+
static_cast<SwOleClient*>(pCli)->SetInDoVerb( true );
CalcAndSetScale( xRef );
diff --git a/tools/CppunitTest_tools_test.mk b/tools/CppunitTest_tools_test.mk
index 5672be53353f..f9e5886723c0 100644
--- a/tools/CppunitTest_tools_test.mk
+++ b/tools/CppunitTest_tools_test.mk
@@ -41,6 +41,7 @@ $(eval $(call gb_CppunitTest_use_libraries,tools_test, \
tl \
test \
unotest \
+ vcl \
))
$(eval $(call gb_CppunitTest_use_static_libraries,tools_test, \
diff --git a/tools/qa/cppunit/test_stream.cxx b/tools/qa/cppunit/test_stream.cxx
index d8e4d1ef71e1..0e025b9a533f 100644
--- a/tools/qa/cppunit/test_stream.cxx
+++ b/tools/qa/cppunit/test_stream.cxx
@@ -27,6 +27,7 @@ namespace
void test_read_cstring();
void test_read_pstring();
void test_readline();
+ void test_makereadonly();
CPPUNIT_TEST_SUITE(Test);
CPPUNIT_TEST(test_stdstream);
@@ -34,6 +35,7 @@ namespace
CPPUNIT_TEST(test_read_cstring);
CPPUNIT_TEST(test_read_pstring);
CPPUNIT_TEST(test_readline);
+ CPPUNIT_TEST(test_makereadonly);
CPPUNIT_TEST_SUITE_END();
};
@@ -271,6 +273,37 @@ namespace
CPPUNIT_ASSERT(issB.eof()); //<-- diff A
}
+ void Test::test_makereadonly()
+ {
+ SvMemoryStream aMemStream;
+ CPPUNIT_ASSERT(aMemStream.IsWritable());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(0), aMemStream.GetSize());
+ aMemStream.WriteInt64(21);
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aMemStream.GetError());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(8), aMemStream.GetSize());
+
+ aMemStream.MakeReadOnly();
+ CPPUNIT_ASSERT(!aMemStream.IsWritable());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(8), aMemStream.GetSize());
+ aMemStream.WriteInt64(42);
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_IO_CANTWRITE, aMemStream.GetError());
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(8), aMemStream.GetSize());
+
+ aMemStream.ResetError();
+ // Check that seeking past the end doesn't resize a read-only stream.
+ // This apparently doesn't set an error, but at least it shouldn't
+ // change the size.
+ aMemStream.Seek(1024LL*1024*1024*3);
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(8), aMemStream.GetSize());
+
+ aMemStream.ResetError();
+ aMemStream.Seek(0);
+ sal_Int64 res;
+ aMemStream.ReadInt64(res);
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aMemStream.GetError());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(21), res);
+ }
+
CPPUNIT_TEST_SUITE_REGISTRATION(Test);
}
diff --git a/tools/source/stream/stream.cxx b/tools/source/stream/stream.cxx
index 404b9eb67537..f0fd3a9de7ec 100644
--- a/tools/source/stream/stream.cxx
+++ b/tools/source/stream/stream.cxx
@@ -1903,6 +1903,14 @@ void SvMemoryStream::SetSize(sal_uInt64 const nNewSize)
ReAllocateMemory( nDiff );
}
+void SvMemoryStream::MakeReadOnly()
+{
+ FlushBuffer();
+ m_isWritable = false;
+ nResize = 0;
+ SetBufferSize( 0 );
+}
+
//Create an OString of nLen bytes from rStream
OString read_uInt8s_ToOString(SvStream& rStrm, std::size_t nLen)
{
diff --git a/unotest/Library_unotest.mk b/unotest/Library_unotest.mk
index ea697699ee82..3acfe2f69ed9 100644
--- a/unotest/Library_unotest.mk
+++ b/unotest/Library_unotest.mk
@@ -22,6 +22,7 @@ $(eval $(call gb_Library_use_libraries,unotest,\
cppuhelper \
sal \
sb \
+ utl \
))
$(eval $(call gb_Library_use_externals,unotest,\
diff --git a/unotest/source/cpp/macros_test.cxx b/unotest/source/cpp/macros_test.cxx
index 20c56950fcaf..37b84cfab8c9 100644
--- a/unotest/source/cpp/macros_test.cxx
+++ b/unotest/source/cpp/macros_test.cxx
@@ -14,11 +14,14 @@
#include <com/sun/star/document/MacroExecMode.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/frame/DispatchHelper.hpp>
+#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
#include <basic/basrdll.hxx>
#include <cppunit/TestAssert.h>
#include <comphelper/sequence.hxx>
#include <comphelper/processfactory.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
using namespace css;
@@ -76,6 +79,20 @@ void MacrosTest::dispatchCommand(const uno::Reference<lang::XComponent>& xCompon
xDispatchHelper->executeDispatch(xFrame, rCommand, OUString(), 0, rPropertyValues);
}
+
+std::unique_ptr<SvStream> MacrosTest::parseExportStream(const utl::TempFile& rTempFile,
+ const OUString& rStreamName)
+{
+ const OUString aUrl = rTempFile.GetURL();
+ uno::Reference<uno::XComponentContext> xComponentContext
+ = comphelper::getProcessComponentContext();
+ uno::Reference<packages::zip::XZipFileAccess2> const xZipNames(
+ packages::zip::ZipFileAccess::createWithURL(xComponentContext, aUrl));
+ uno::Reference<io::XInputStream> const xInputStream(xZipNames->getByName(rStreamName),
+ uno::UNO_QUERY);
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ return pStream;
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/errinf.cxx b/vcl/source/window/errinf.cxx
index 8e08dc361acd..80c9c36ee897 100644
--- a/vcl/source/window/errinf.cxx
+++ b/vcl/source/window/errinf.cxx
@@ -54,6 +54,7 @@ bool ErrorStringFactory::CreateString(const ErrorInfo* pInfo, OUString& rStr)
ErrorRegistry::ErrorRegistry()
: pDsp(nullptr)
, bIsWindowDsp(false)
+ , m_bLock(false)
, nNextError(0)
{
for(DynamicErrorInfo*& rp : ppDynErrInfo)
@@ -74,6 +75,18 @@ void ErrorRegistry::RegisterDisplay(WindowDisplayErrorFunc *aDsp)
rData.pDsp = reinterpret_cast< DisplayFnPtr >(aDsp);
}
+void ErrorRegistry::SetLock(bool bLock)
+{
+ ErrorRegistry &rData = TheErrorRegistry::get();
+ rData.m_bLock = bLock;
+}
+
+bool ErrorRegistry::GetLock()
+{
+ ErrorRegistry &rData = TheErrorRegistry::get();
+ return rData.m_bLock;
+}
+
void ErrorRegistry::Reset()
{
ErrorRegistry &rData = TheErrorRegistry::get();
@@ -160,7 +173,7 @@ DialogMask ErrorHandler::HandleError(ErrCode nErrCodeId, weld::Window *pParent,
OUString aErr;
if (ErrorStringFactory::CreateString(pInfo.get(), aErr))
{
- if(!rData.pDsp)
+ if (!rData.pDsp || rData.m_bLock)
{
SAL_WARN( "vcl", "Action: " << aAction << "Error: " << aErr);
}
diff --git a/xmloff/CppunitTest_xmloff_text.mk b/xmloff/CppunitTest_xmloff_text.mk
index e3259672605b..a61f381f11a0 100644
--- a/xmloff/CppunitTest_xmloff_text.mk
+++ b/xmloff/CppunitTest_xmloff_text.mk
@@ -13,6 +13,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,xmloff_text))
$(eval $(call gb_CppunitTest_use_externals,xmloff_text,\
boost_headers \
+ libxml2 \
))
$(eval $(call gb_CppunitTest_add_exception_objects,xmloff_text, \
@@ -26,6 +27,7 @@ $(eval $(call gb_CppunitTest_use_libraries,xmloff_text, \
sal \
test \
unotest \
+ utl \
))
$(eval $(call gb_CppunitTest_use_sdk_api,xmloff_text))
diff --git a/xmloff/qa/unit/data/list-id.fodt b/xmloff/qa/unit/data/list-id.fodt
new file mode 100644
index 000000000000..377dbcbd6473
--- /dev/null
+++ b/xmloff/qa/unit/data/list-id.fodt
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<office:document xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:automatic-styles>
+ <text:list-style style:name="L1">
+ <text:list-level-style-number text:level="1" text:style-name="Numbering_20_Symbols" style:num-suffix="." style:num-format="1"/>
+ </text:list-style>
+ </office:automatic-styles>
+ <office:body>
+ <office:text>
+ <text:list text:style-name="L1">
+ <text:list-item>
+ <text:p>First</text:p>
+ </text:list-item>
+ <text:list-item>
+ <text:p>Second</text:p>
+ </text:list-item>
+ <text:list-item>
+ <text:p>Third</text:p>
+ </text:list-item>
+ </text:list>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/xmloff/qa/unit/style.cxx b/xmloff/qa/unit/style.cxx
index f859c3619837..1ccffa8a039c 100644
--- a/xmloff/qa/unit/style.cxx
+++ b/xmloff/qa/unit/style.cxx
@@ -19,6 +19,7 @@
#include <comphelper/propertysequence.hxx>
#include <unotools/tempfile.hxx>
#include <unotools/ucbstreamhelper.hxx>
+#include <rtl/character.hxx>
using namespace ::com::sun::star;
@@ -82,6 +83,25 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testFillImageBase64)
CPPUNIT_ASSERT(xBitmaps->hasByName("libreoffice_0"));
}
+namespace
+{
+struct XmlFont
+{
+ OString aName;
+ OString aFontFamilyGeneric;
+ bool operator<(const XmlFont& rOther) const
+ {
+ sal_Int32 nRet = aName.compareTo(rOther.aName);
+ if (nRet != 0)
+ {
+ return nRet < 0;
+ }
+
+ return aFontFamilyGeneric.compareTo(rOther.aFontFamilyGeneric) < 0;
+ }
+};
+}
+
CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testFontSorting)
{
// Given an empty document with default fonts (Liberation Sans, Lucida Sans, etc):
@@ -107,25 +127,46 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testFontSorting)
= getXPathNode(pXmlDoc, "/office:document-content/office:font-face-decls/style:font-face");
xmlNodeSetPtr pXmlNodes = pXPath->nodesetval;
int nNodeCount = xmlXPathNodeSetGetLength(pXmlNodes);
- std::vector<OString> aXMLNames;
- std::set<OString> aSortedNames;
+ std::vector<XmlFont> aXMLFonts;
+ std::vector<XmlFont> aSortedFonts;
for (int i = 0; i < nNodeCount; ++i)
{
xmlNodePtr pXmlNode = pXmlNodes->nodeTab[i];
xmlChar* pName = xmlGetProp(pXmlNode, BAD_CAST("name"));
OString aName(reinterpret_cast<char const*>(pName));
- aXMLNames.push_back(aName);
- aSortedNames.insert(aName);
+
+ // Ignore numbers at the end, those are just appended to make all names unique.
+ while (rtl::isAsciiDigit(static_cast<sal_uInt32>(aName[aName.getLength() - 1])))
+ {
+ aName = aName.copy(0, aName.getLength() - 1);
+ }
+
+ xmlChar* pFontFamilyGeneric = xmlGetProp(pXmlNode, BAD_CAST("font-family-generic"));
+ OString aFontFamilyGeneric;
+ if (pFontFamilyGeneric)
+ {
+ aFontFamilyGeneric = OString(reinterpret_cast<char const*>(pFontFamilyGeneric));
+ }
+
+ aXMLFonts.push_back(XmlFont{ aName, aFontFamilyGeneric });
+ aSortedFonts.push_back(XmlFont{ aName, aFontFamilyGeneric });
xmlFree(pName);
}
+ std::sort(aSortedFonts.begin(), aSortedFonts.end());
size_t nIndex = 0;
- for (const auto& rName : aSortedNames)
+ for (const auto& rFont : aSortedFonts)
{
// Without the accompanying fix in place, this test would have failed with:
// - Expected: Liberation Sans
// - Actual : Lucida Sans1
// i.e. the output was not lexicographically sorted, "u" was before "i".
- CPPUNIT_ASSERT_EQUAL(rName, aXMLNames[nIndex]);
+ CPPUNIT_ASSERT_EQUAL(rFont.aName, aXMLFonts[nIndex].aName);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: swiss
+ // - Actual : system
+ // i.e. the output was not lexicographically sorted when style:name was the same, but
+ // style:font-family-generic was not the same.
+ CPPUNIT_ASSERT_EQUAL(rFont.aFontFamilyGeneric, aXMLFonts[nIndex].aFontFamilyGeneric);
++nIndex;
}
xmlXPathFreeObject(pXPath);
diff --git a/xmloff/qa/unit/text.cxx b/xmloff/qa/unit/text.cxx
index 2a5c65e5c2d9..9bd802e813d5 100644
--- a/xmloff/qa/unit/text.cxx
+++ b/xmloff/qa/unit/text.cxx
@@ -9,16 +9,23 @@
#include <test/bootstrapfixture.hxx>
#include <unotest/macros_test.hxx>
+#include <test/xmltesttools.hxx>
#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <unotools/tempfile.hxx>
+
using namespace ::com::sun::star;
char const DATA_DIRECTORY[] = "/xmloff/qa/unit/data/";
/// Covers xmloff/source/text/ fixes.
-class XmloffStyleTest : public test::BootstrapFixture, public unotest::MacrosTest
+class XmloffStyleTest : public test::BootstrapFixture,
+ public unotest::MacrosTest,
+ public XmlTestTools
{
private:
uno::Reference<lang::XComponent> mxComponent;
@@ -26,9 +33,15 @@ private:
public:
void setUp() override;
void tearDown() override;
+ void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override;
uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
};
+void XmloffStyleTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx)
+{
+ XmlTestTools::registerODFNamespaces(pXmlXpathCtx);
+}
+
void XmloffStyleTest::setUp()
{
test::BootstrapFixture::setUp();
@@ -53,6 +66,30 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testMailMergeInEditeng)
CPPUNIT_ASSERT(getComponent().is());
}
+CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testListId)
+{
+ // Given a document with a simple list (no continue-list="..." attribute):
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "list-id.fodt";
+ getComponent() = loadFromDesktop(aURL);
+
+ // When storing that document as ODF:
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aStoreProps = comphelper::InitPropertySequence({
+ { "FilterName", uno::makeAny(OUString("writer8")) },
+ });
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aStoreProps);
+
+ // Then make sure that unreferenced xml:id="..." attributes are not written:
+ std::unique_ptr<SvStream> pStream = parseExportStream(aTempFile, "content.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ // Without the accompanying fix in place, this failed with:
+ // - XPath '//text:list' unexpected 'id' attribute
+ // i.e. xml:id="..." was written unconditionally, even when no other list needed it.
+ assertXPathNoAttribute(pXmlDoc, "//text:list", "id");
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/core/xmlimp.cxx b/xmloff/source/core/xmlimp.cxx
index 1e77eca85928..e938deb617d1 100644
--- a/xmloff/source/core/xmlimp.cxx
+++ b/xmloff/source/core/xmlimp.cxx
@@ -655,6 +655,8 @@ void SAL_CALL SvXMLImport::endDocument()
}
std::unique_ptr<SvXMLNamespaceMap> SvXMLImport::processNSAttributes(
+ std::unique_ptr<SvXMLNamespaceMap> & rpNamespaceMap,
+ SvXMLImport *const pImport, // TODO???
const uno::Reference< xml::sax::XAttributeList >& xAttrList)
{
std::unique_ptr<SvXMLNamespaceMap> pRewindMap;
@@ -662,12 +664,13 @@ std::unique_ptr<SvXMLNamespaceMap> SvXMLImport::processNSAttributes(
for( sal_Int16 i=0; i < nAttrCount; i++ )
{
const OUString& rAttrName = xAttrList->getNameByIndex( i );
- if ( rAttrName == "office:version" )
+ if (pImport && rAttrName == "office:version")
{
- mpImpl->aODFVersion = xAttrList->getValueByIndex( i );
+ pImport->mpImpl->aODFVersion = xAttrList->getValueByIndex( i );
// the ODF version in content.xml and manifest.xml must be the same starting from ODF1.2
- if ( mpImpl->mStreamName == "content.xml" && !IsODFVersionConsistent( mpImpl->aODFVersion ) )
+ if (pImport->mpImpl->mStreamName == "content.xml"
+ && !pImport->IsODFVersionConsistent(pImport->mpImpl->aODFVersion))
{
throw xml::sax::SAXException("Inconsistent ODF versions in content.xml and manifest.xml!",
uno::Reference< uno::XInterface >(),
@@ -681,8 +684,8 @@ std::unique_ptr<SvXMLNamespaceMap> SvXMLImport::processNSAttributes(
{
if( !pRewindMap )
{
- pRewindMap = std::move(mpNamespaceMap);
- mpNamespaceMap.reset(new SvXMLNamespaceMap(*pRewindMap));
+ pRewindMap = std::move(rpNamespaceMap);
+ rpNamespaceMap.reset(new SvXMLNamespaceMap(*pRewindMap));
}
const OUString& rAttrValue = xAttrList->getValueByIndex( i );
@@ -690,18 +693,18 @@ std::unique_ptr<SvXMLNamespaceMap> SvXMLImport::processNSAttributes(
? OUString()
: rAttrName.copy( 6 ) );
// Add namespace, but only if it is known.
- sal_uInt16 nKey = mpNamespaceMap->AddIfKnown( aPrefix, rAttrValue );
+ sal_uInt16 nKey = rpNamespaceMap->AddIfKnown( aPrefix, rAttrValue );
// If namespace is unknown, try to match a name with similar
// TC Id and version
if( XML_NAMESPACE_UNKNOWN == nKey )
{
OUString aTestName( rAttrValue );
if( SvXMLNamespaceMap::NormalizeURI( aTestName ) )
- nKey = mpNamespaceMap->AddIfKnown( aPrefix, aTestName );
+ nKey = rpNamespaceMap->AddIfKnown( aPrefix, aTestName );
}
// If that namespace is not known, too, add it as unknown
if( XML_NAMESPACE_UNKNOWN == nKey )
- mpNamespaceMap->Add( aPrefix, rAttrValue );
+ rpNamespaceMap->Add( aPrefix, rAttrValue );
}
}
@@ -714,7 +717,8 @@ void SAL_CALL SvXMLImport::startElement( const OUString& rName,
// SAL_INFO("svg", "startElement " << rName);
// Process namespace attributes. This must happen before creating the
// context, because namespace declaration apply to the element name itself.
- std::unique_ptr<SvXMLNamespaceMap> pRewindMap(processNSAttributes(xAttrList));
+ std::unique_ptr<SvXMLNamespaceMap> pRewindMap(
+ processNSAttributes(mpNamespaceMap, this, xAttrList));
// Get element's namespace and local name.
OUString aLocalName;
@@ -888,7 +892,7 @@ void SAL_CALL SvXMLImport::startFastElement (sal_Int32 Element,
maNamespaceHandler->addNSDeclAttributes( maNamespaceAttrList );
std::unique_ptr<SvXMLNamespaceMap> pRewindMap(
- processNSAttributes( maNamespaceAttrList.get() ));
+ processNSAttributes(mpNamespaceMap, this, maNamespaceAttrList.get()));
assert( dynamic_cast<SvXMLImportContext*>( xContext.get() ) != nullptr );
SvXMLImportContext *pContext = static_cast<SvXMLImportContext*>( xContext.get() );
if (pRewindMap)
@@ -2241,7 +2245,7 @@ void SAL_CALL SvXMLLegacyToFastDocHandler::endDocument()
void SAL_CALL SvXMLLegacyToFastDocHandler::startElement( const OUString& rName,
const uno::Reference< xml::sax::XAttributeList >& xAttrList )
{
- mrImport->processNSAttributes(xAttrList);
+ SvXMLImport::processNSAttributes(mrImport->mpNamespaceMap, mrImport.get(), xAttrList);
OUString aLocalName;
sal_uInt16 nPrefix = mrImport->mpNamespaceMap->GetKeyByAttrName( rName, &aLocalName );
Sequence< sal_Int8 > aLocalNameSeq( reinterpret_cast<sal_Int8 const *>(
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index 8d9a70f5e082..91c014f5756f 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -144,6 +144,19 @@ namespace xmloff::token {
TOKEN( "grddl", XML_NP_GRDDL ),
TOKEN( "http://www.w3.org/2003/g/data-view#", XML_N_GRDDL ),
+ // OOo extension digital signatures, used in ODF 1.1
+ TOKEN( "dsigooo", XML_NP_DSIG_OOO ),
+ TOKEN( "http://openoffice.org/2004/documentsignatures", XML_N_DSIG_OOO ),
+ // ODF 1.2 digital signature namespaces
+ TOKEN( "dsig", XML_NP_DSIG ),
+ TOKEN( "urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0", XML_N_DSIG ),
+ TOKEN( "ds", XML_NP_DS ),
+ TOKEN( "http://www.w3.org/2000/09/xmldsig#", XML_N_DS ),
+ TOKEN( "xades132", XML_NP_XADES132 ),
+ TOKEN( "http://uri.etsi.org/01903/v1.3.2#", XML_N_XADES132 ),
+ TOKEN( "xades141", XML_NP_XADES141 ),
+ TOKEN( "http://uri.etsi.org/01903/v1.4.1#", XML_N_XADES141 ),
+
// ODF Enhanced namespaces
TOKEN( "officeooo", XML_NP_OFFICE_EXT ),
TOKEN( "http://openoffice.org/2009/office", XML_N_OFFICE_EXT ),
diff --git a/xmloff/source/text/XMLTextNumRuleInfo.cxx b/xmloff/source/text/XMLTextNumRuleInfo.cxx
index b982d4d6dd4b..6330822e466f 100644
--- a/xmloff/source/text/XMLTextNumRuleInfo.cxx
+++ b/xmloff/source/text/XMLTextNumRuleInfo.cxx
@@ -21,6 +21,7 @@
#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/container/XIndexReplace.hpp>
#include <com/sun/star/style/NumberingType.hpp>
@@ -40,6 +41,7 @@ XMLTextNumRuleInfo::XMLTextNumRuleInfo()
: mxNumRules()
, msNumRulesName()
, msListId()
+ , mbListIdIsDefault(false)
, mnListStartValue( -1 )
, mnListLevel( 0 )
, mbIsNumbered( false )
@@ -62,6 +64,7 @@ void XMLTextNumRuleInfo::Set(
mbOutlineStyleAsNormalListStyle = bOutlineStyleAsNormalListStyle;
Reference< XPropertySet > xPropSet( xTextContent, UNO_QUERY );
+ Reference<XPropertyState> xPropState(xTextContent, UNO_QUERY);
Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
// check if this paragraph supports a numbering
@@ -138,6 +141,12 @@ void XMLTextNumRuleInfo::Set(
if( xPropSetInfo->hasPropertyByName( "ListId" ) )
{
xPropSet->getPropertyValue( "ListId" ) >>= msListId;
+
+ if (xPropState.is())
+ {
+ mbListIdIsDefault
+ = xPropState->getPropertyState("ListId") == PropertyState_DEFAULT_VALUE;
+ }
}
mbContinueingPreviousSubTree = false;
diff --git a/xmloff/source/text/XMLTextNumRuleInfo.hxx b/xmloff/source/text/XMLTextNumRuleInfo.hxx
index 28555f88af26..ff0f45745dc3 100644
--- a/xmloff/source/text/XMLTextNumRuleInfo.hxx
+++ b/xmloff/source/text/XMLTextNumRuleInfo.hxx
@@ -44,6 +44,8 @@ class XMLTextNumRuleInfo
// paragraph's list attributes
OUString msListId;
+ /// msListId won't be referenced by later lists.
+ bool mbListIdIsDefault;
sal_Int16 mnListStartValue;
sal_Int16 mnListLevel;
bool mbIsNumbered;
@@ -84,6 +86,8 @@ public:
return msListId;
}
+ bool IsListIdDefault() const { return mbListIdIsDefault; }
+
sal_Int16 GetLevel() const
{
return mnListLevel;
diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx
index d0c067ac7ed3..c5204c9f4504 100644
--- a/xmloff/source/text/txtparae.cxx
+++ b/xmloff/source/text/txtparae.cxx
@@ -965,7 +965,7 @@ void XMLTextParagraphExport::exportListChange(
{
if ( bExportODF &&
eODFDefaultVersion >= SvtSaveOptions::ODFSVER_012 &&
- !sListId.isEmpty() )
+ !sListId.isEmpty() && !rNextInfo.IsListIdDefault() )
{
/* Property text:id at element <text:list> has to be
replaced by property xml:id (#i92221#)
@@ -984,7 +984,7 @@ void XMLTextParagraphExport::exportListChange(
mpTextListsHelper->GenerateNewListId() );
if ( bExportODF &&
eODFDefaultVersion >= SvtSaveOptions::ODFSVER_012 &&
- !sListId.isEmpty() )
+ !sListId.isEmpty() && !rNextInfo.IsListIdDefault() )
{
/* Property text:id at element <text:list> has to be
replaced by property xml:id (#i92221#)
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index 34b9af91e03c..495e519ffb2d 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -72,6 +72,16 @@ xhtml
N_XHTML_DUMMY
grddl
N_GRDDL_DUMMY
+dsigooo
+N_DSIG_OOO_DUMMY
+dsig
+N_DSIG_DUMMY
+ds
+N_DS_DUMMY
+xades132
+N_XADES132_DUMMY
+xades141
+N_XADES141_DUMMY
officeooo
N_OFFICE_EXT_DUMMY
formx
diff --git a/xmlsecurity/inc/biginteger.hxx b/xmlsecurity/inc/biginteger.hxx
index d07ecf45d8af..8b4d8a9143b5 100644
--- a/xmlsecurity/inc/biginteger.hxx
+++ b/xmlsecurity/inc/biginteger.hxx
@@ -31,6 +31,9 @@ namespace xmlsecurity
{
XSECXMLSEC_DLLPUBLIC OUString bigIntegerToNumericString( const css::uno::Sequence< sal_Int8 >& serial );
XSECXMLSEC_DLLPUBLIC css::uno::Sequence< sal_Int8 > numericStringToBigInteger ( const OUString& serialNumber );
+
+XSECXMLSEC_DLLPUBLIC bool EqualDistinguishedNames(OUString const& rName1,
+ OUString const& rName2);
}
#endif
diff --git a/xmlsecurity/inc/xmlsec-wrapper.h b/xmlsecurity/inc/xmlsec-wrapper.h
index c060c8bf23b8..2d06dcfdd549 100644
--- a/xmlsecurity/inc/xmlsec-wrapper.h
+++ b/xmlsecurity/inc/xmlsec-wrapper.h
@@ -43,6 +43,10 @@
#include <xmlsec/nss/app.h>
#include <xmlsec/nss/crypto.h>
#include <xmlsec/nss/pkikeys.h>
+#include <xmlsec/nss/x509.h>
+#endif
+#ifdef XMLSEC_CRYPTO_MSCRYPTO
+#include <xmlsec/mscng/x509.h>
#endif
#endif
diff --git a/xmlsecurity/inc/xmlsignaturehelper.hxx b/xmlsecurity/inc/xmlsignaturehelper.hxx
index 2437686ea31b..a12309ef4d0a 100644
--- a/xmlsecurity/inc/xmlsignaturehelper.hxx
+++ b/xmlsecurity/inc/xmlsignaturehelper.hxx
@@ -28,6 +28,9 @@
#include "xmlsignaturehelper.hxx"
#include "xsecctl.hxx"
+#include <com/sun/star/security/XCertificate.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+
class DateTime;
class UriBindingHelper;
@@ -93,6 +96,15 @@ public:
// After signing/verifying, get information about signatures
SignatureInformation GetSignatureInformation( sal_Int32 nSecurityId ) const;
SignatureInformations GetSignatureInformations() const;
+ /// ImplVerifySignature calls this to figure out which X509Data is the
+ /// signing certificate and update the internal state with the result.
+ /// @return
+ /// A sequence with the signing certificate at the back on success.
+ /// An empty sequence on failure.
+ std::vector<css::uno::Reference<css::security::XCertificate>>
+ CheckAndUpdateSignatureInformation(
+ css::uno::Reference<css::xml::crypto::XSecurityEnvironment> const& xSecEnv,
+ SignatureInformation const& rInfo);
// See XSecController for documentation
void StartMission(const css::uno::Reference<css::xml::crypto::XXMLSecurityContext>& xSecurityContext);
diff --git a/xmlsecurity/inc/xsecctl.hxx b/xmlsecurity/inc/xsecctl.hxx
index 324f1c43388b..ca6c169798c2 100644
--- a/xmlsecurity/inc/xsecctl.hxx
+++ b/xmlsecurity/inc/xsecctl.hxx
@@ -252,6 +252,7 @@ private:
/// Sets algorithm from <SignatureMethod Algorithm="...">.
void setSignatureMethod(svl::crypto::SignatureMethodAlgorithm eAlgorithmID);
void switchGpgSignature();
+ bool haveReferenceForId(OUString const& rId) const;
void addReference(
const OUString& ouUri,
sal_Int32 nDigestID,
@@ -262,18 +263,21 @@ private:
sal_Int32 nDigestID );
void setReferenceCount() const;
- void setX509IssuerName( OUString const & ouX509IssuerName );
- void setX509SerialNumber( OUString const & ouX509SerialNumber );
- void setX509Certificate( OUString const & ouX509Certificate );
+ void setX509Data(
+ std::vector<std::pair<OUString, OUString>> & rX509IssuerSerials,
+ std::vector<OUString> const& rX509Certificates);
+ void setX509CertDigest(
+ OUString const& rCertDigest, sal_Int32 const nReferenceDigestID,
+ OUString const& rX509IssuerName, OUString const& rX509SerialNumber);
+
void setSignatureValue( OUString const & ouSignatureValue );
void setDigestValue( sal_Int32 nDigestID, OUString const & ouDigestValue );
void setGpgKeyID( OUString const & ouKeyID );
void setGpgCertificate( OUString const & ouGpgCert );
void setGpgOwner( OUString const & ouGpgOwner );
- void setDate( OUString const & ouDate );
- void setDescription(const OUString& rDescription);
- void setCertDigest(const OUString& rCertDigest);
+ void setDate(OUString const& rId, OUString const& ouDate);
+ void setDescription(OUString const& rId, OUString const& rDescription);
void setValidSignatureImage(const OUString& rValidSigImg);
void setInvalidSignatureImage(const OUString& rInvalidSigImg);
void setSignatureLineId(const OUString& rSignatureLineId);
@@ -283,7 +287,6 @@ public:
private:
void setId( OUString const & ouId );
- void setPropertyId( OUString const & ouPropertyId );
css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > prepareSignatureToRead(
sal_Int32 nSecurityId );
@@ -303,6 +306,9 @@ public:
SignatureInformation getSignatureInformation( sal_Int32 nSecurityId ) const;
SignatureInformations getSignatureInformations() const;
+ /// only verify can figure out which X509Data is the signing certificate
+ void UpdateSignatureInformation(sal_Int32 nSecurityId,
+ std::vector<SignatureInformation::X509Data> const& rDatas);
static void exportSignature(
const css::uno::Reference< css::xml::sax::XDocumentHandler >& xDocumentHandler,
diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx
index d149bf5e30b2..2f57db7fcaf4 100644
--- a/xmlsecurity/source/component/documentdigitalsignatures.cxx
+++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx
@@ -19,9 +19,10 @@
#include <resourcemanager.hxx>
-#include <digitalsignaturesdialog.hxx>
+#include <certificate.hxx>
#include <certificatechooser.hxx>
#include <certificateviewer.hxx>
+#include <digitalsignaturesdialog.hxx>
#include <macrosecurity.hxx>
#include <biginteger.hxx>
#include <strings.hrc>
@@ -529,30 +530,36 @@ DocumentDigitalSignatures::ImplVerifySignatures(
const SignatureInformation& rInfo = aSignInfos[n];
css::security::DocumentSignatureInformation& rSigInfo = arInfos[n];
- if (rInfo.ouGpgCertificate.isEmpty()) // X.509
+ if (!rInfo.X509Datas.empty()) // X.509
{
- if (!rInfo.ouX509Certificate.isEmpty())
- rSigInfo.Signer = xSecEnv->createCertificateFromAscii(rInfo.ouX509Certificate);
- if (!rSigInfo.Signer.is())
- rSigInfo.Signer = xSecEnv->getCertificate(
- rInfo.ouX509IssuerName,
- xmlsecurity::numericStringToBigInteger(rInfo.ouX509SerialNumber));
-
- // On Windows checking the certificate path is buggy. It does name matching (issuer, subject name)
- // to find the parent certificate. It does not take into account that there can be several certificates
- // with the same subject name.
- try
+ std::vector<uno::Reference<XCertificate>> certs(
+ rSignatureHelper.CheckAndUpdateSignatureInformation(
+ xSecEnv, rInfo));
+ if (certs.empty())
{
- rSigInfo.CertificateStatus = xSecEnv->verifyCertificate(
- rSigInfo.Signer, Sequence<Reference<css::security::XCertificate>>());
+ rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID;
}
- catch (SecurityException&)
+ else
{
- OSL_FAIL("Verification of certificate failed");
- rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID;
+ rSigInfo.Signer = certs.back();
+ // get only intermediates
+ certs.pop_back();
+ // On Windows checking the certificate path is buggy. It does name matching (issuer, subject name)
+ // to find the parent certificate. It does not take into account that there can be several certificates
+ // with the same subject name.
+ try
+ {
+ rSigInfo.CertificateStatus = xSecEnv->verifyCertificate(
+ rSigInfo.Signer, comphelper::containerToSequence(certs));
+ }
+ catch (SecurityException&)
+ {
+ SAL_WARN("xmlsecurity.comp", "Verification of certificate failed");
+ rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID;
+ }
}
}
- else if (xGpgSecEnv.is()) // GPG
+ else if (!rInfo.ouGpgCertificate.isEmpty() && xGpgSecEnv.is()) // GPG
{
// TODO not ideal to retrieve cert by keyID, might
// collide, or PGPKeyID format might change - can't we
@@ -638,16 +645,34 @@ void DocumentDigitalSignatures::showCertificate(
}
sal_Bool DocumentDigitalSignatures::isAuthorTrusted(
- const Reference< css::security::XCertificate >& Author )
+ const Reference<css::security::XCertificate>& xAuthor)
{
- OUString sSerialNum = xmlsecurity::bigIntegerToNumericString( Author->getSerialNumber() );
+ if (!xAuthor.is())
+ {
+ return false;
+ }
+ OUString sSerialNum = xmlsecurity::bigIntegerToNumericString(xAuthor->getSerialNumber());
Sequence< SvtSecurityOptions::Certificate > aTrustedAuthors = SvtSecurityOptions().GetTrustedAuthors();
return std::any_of(aTrustedAuthors.begin(), aTrustedAuthors.end(),
- [&Author, &sSerialNum](const SvtSecurityOptions::Certificate& rAuthor) {
- return ( rAuthor[0] == Author->getIssuerName() )
- && ( rAuthor[1] == sSerialNum );
+ [this, &xAuthor, &sSerialNum](const SvtSecurityOptions::Certificate& rAuthor) {
+ if (!xmlsecurity::EqualDistinguishedNames(rAuthor[0], xAuthor->getIssuerName()))
+ return false;
+ if (rAuthor[1] != sSerialNum)
+ return false;
+
+ DocumentSignatureManager aSignatureManager(mxCtx, {});
+ if (!aSignatureManager.init())
+ return false;
+ uno::Reference<css::security::XCertificate> xCert = aSignatureManager.getSecurityEnvironment()->createCertificateFromAscii(rAuthor[2]);
+
+ auto pAuthor = dynamic_cast<xmlsecurity::Certificate*>(xAuthor.get());
+ auto pCert = dynamic_cast<xmlsecurity::Certificate*>(xCert.get());
+ if (pAuthor && pCert)
+ return pCert->getSHA256Thumbprint() == pAuthor->getSHA256Thumbprint();
+
+ return xCert->getSHA1Thumbprint() == xAuthor->getSHA1Thumbprint();
});
}
diff --git a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
index d90bd33f9cfa..af3c4ca4de7c 100644
--- a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
+++ b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
@@ -587,7 +587,7 @@ void DigitalSignaturesDialog::ImplFillSignaturesBox()
if (!rInfo.ouGpgCertificate.isEmpty())
aType = "OpenPGP";
// XML based: XAdES or not.
- else if (!rInfo.ouCertDigest.isEmpty())
+ else if (rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->CertDigest.isEmpty())
aType = "XAdES";
else
aType = "XML-DSig";
@@ -686,8 +686,8 @@ uno::Reference<security::XCertificate> DigitalSignaturesDialog::getCertificate(c
uno::Reference<security::XCertificate> xCert;
//First we try to get the certificate which is embedded in the XML Signature
- if (xSecEnv.is() && !rInfo.ouX509Certificate.isEmpty())
- xCert = xSecEnv->createCertificateFromAscii(rInfo.ouX509Certificate);
+ if (xSecEnv.is() && rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->X509Certificate.isEmpty())
+ xCert = xSecEnv->createCertificateFromAscii(rInfo.GetSigningCertificate()->X509Certificate);
else {
//There must be an embedded certificate because we use it to get the
//issuer name. We cannot use /Signature/KeyInfo/X509Data/X509IssuerName
@@ -699,9 +699,12 @@ uno::Reference<security::XCertificate> DigitalSignaturesDialog::getCertificate(c
}
//In case there is no embedded certificate we try to get it from a local store
- if (!xCert.is() && xSecEnv.is())
- xCert = xSecEnv->getCertificate( rInfo.ouX509IssuerName, xmlsecurity::numericStringToBigInteger( rInfo.ouX509SerialNumber ) );
- if (!xCert.is() && xGpgSecEnv.is())
+ if (!xCert.is() && xSecEnv.is() && rInfo.GetSigningCertificate())
+ {
+ xCert = xSecEnv->getCertificate(rInfo.GetSigningCertificate()->X509IssuerName,
+ xmlsecurity::numericStringToBigInteger(rInfo.GetSigningCertificate()->X509SerialNumber));
+ }
+ if (!xCert.is() && xGpgSecEnv.is() && !rInfo.ouGpgKeyID.isEmpty())
xCert = xGpgSecEnv->getCertificate( rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger("") );
SAL_WARN_IF( !xCert.is(), "xmlsecurity.dialogs", "Certificate not found and can't be created!" );
diff --git a/xmlsecurity/source/helper/documentsignaturehelper.cxx b/xmlsecurity/source/helper/documentsignaturehelper.cxx
index 482ae6cc4126..ddff308ee52f 100644
--- a/xmlsecurity/source/helper/documentsignaturehelper.cxx
+++ b/xmlsecurity/source/helper/documentsignaturehelper.cxx
@@ -492,6 +492,29 @@ void DocumentSignatureHelper::writeDigestMethod(
xDocumentHandler->endElement("DigestMethod");
}
+static void WriteXadesCert(
+ uno::Reference<xml::sax::XDocumentHandler> const& xDocumentHandler,
+ SignatureInformation::X509CertInfo const& rCertInfo)
+{
+ xDocumentHandler->startElement("xd:Cert", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->startElement("xd:CertDigest", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ DocumentSignatureHelper::writeDigestMethod(xDocumentHandler);
+ xDocumentHandler->startElement("DigestValue", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ assert(!rCertInfo.CertDigest.isEmpty());
+ xDocumentHandler->characters(rCertInfo.CertDigest);
+ xDocumentHandler->endElement("DigestValue");
+ xDocumentHandler->endElement("xd:CertDigest");
+ xDocumentHandler->startElement("xd:IssuerSerial", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->startElement("X509IssuerName", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->characters(rCertInfo.X509IssuerName);
+ xDocumentHandler->endElement("X509IssuerName");
+ xDocumentHandler->startElement("X509SerialNumber", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->characters(rCertInfo.X509SerialNumber);
+ xDocumentHandler->endElement("X509SerialNumber");
+ xDocumentHandler->endElement("xd:IssuerSerial");
+ xDocumentHandler->endElement("xd:Cert");
+}
+
void DocumentSignatureHelper::writeSignedProperties(
const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
const SignatureInformation& signatureInfo,
@@ -508,26 +531,26 @@ void DocumentSignatureHelper::writeSignedProperties(
xDocumentHandler->characters(sDate);
xDocumentHandler->endElement("xd:SigningTime");
xDocumentHandler->startElement("xd:SigningCertificate", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- xDocumentHandler->startElement("xd:Cert", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- xDocumentHandler->startElement("xd:CertDigest", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- writeDigestMethod(xDocumentHandler);
-
- xDocumentHandler->startElement("DigestValue", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- // TODO: this is empty for gpg signatures currently
- //assert(!signatureInfo.ouCertDigest.isEmpty());
- xDocumentHandler->characters(signatureInfo.ouCertDigest);
- xDocumentHandler->endElement("DigestValue");
-
- xDocumentHandler->endElement("xd:CertDigest");
- xDocumentHandler->startElement("xd:IssuerSerial", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- xDocumentHandler->startElement("X509IssuerName", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- xDocumentHandler->characters(signatureInfo.ouX509IssuerName);
- xDocumentHandler->endElement("X509IssuerName");
- xDocumentHandler->startElement("X509SerialNumber", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- xDocumentHandler->characters(signatureInfo.ouX509SerialNumber);
- xDocumentHandler->endElement("X509SerialNumber");
- xDocumentHandler->endElement("xd:IssuerSerial");
- xDocumentHandler->endElement("xd:Cert");
+ assert(signatureInfo.GetSigningCertificate() || !signatureInfo.ouGpgKeyID.isEmpty());
+ if (signatureInfo.GetSigningCertificate())
+ {
+ // how should this deal with multiple X509Data elements?
+ // for now, let's write all of the certificates ...
+ for (auto const& rData : signatureInfo.X509Datas)
+ {
+ for (auto const& it : rData)
+ {
+ WriteXadesCert(xDocumentHandler, it);
+ }
+ }
+ }
+ else
+ {
+ // for PGP, write empty mandatory X509IssuerName, X509SerialNumber
+ SignatureInformation::X509CertInfo temp;
+ temp.CertDigest = signatureInfo.ouGpgKeyID;
+ WriteXadesCert(xDocumentHandler, temp);
+ }
xDocumentHandler->endElement("xd:SigningCertificate");
xDocumentHandler->startElement("xd:SignaturePolicyIdentifier", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
xDocumentHandler->startElement("xd:SignaturePolicyImplied", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
diff --git a/xmlsecurity/source/helper/documentsignaturemanager.cxx b/xmlsecurity/source/helper/documentsignaturemanager.cxx
index 79d2cdf26b61..7252c54e74ab 100644
--- a/xmlsecurity/source/helper/documentsignaturemanager.cxx
+++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx
@@ -586,6 +586,18 @@ void DocumentSignatureManager::read(bool bUseTempStream, bool bCacheLastSignatur
bCacheLastSignature);
maSignatureHelper.EndMission();
+ // this parses the XML independently from ImplVerifySignatures() - check
+ // certificates here too ...
+ for (auto const& it : maSignatureHelper.GetSignatureInformations())
+ {
+ if (!it.X509Datas.empty())
+ {
+ uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(
+ getSecurityEnvironment());
+ getSignatureHelper().CheckAndUpdateSignatureInformation(xSecEnv, it);
+ }
+ }
+
maCurrentSignatureInformations = maSignatureHelper.GetSignatureInformations();
}
else
diff --git a/xmlsecurity/source/helper/ooxmlsecexporter.cxx b/xmlsecurity/source/helper/ooxmlsecexporter.cxx
index fe4d0df89a5d..d2c29041338f 100644
--- a/xmlsecurity/source/helper/ooxmlsecexporter.cxx
+++ b/xmlsecurity/source/helper/ooxmlsecexporter.cxx
@@ -200,13 +200,21 @@ void OOXMLSecExporter::Impl::writeKeyInfo()
{
m_xDocumentHandler->startElement(
"KeyInfo", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- m_xDocumentHandler->startElement(
- "X509Data", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- m_xDocumentHandler->startElement(
- "X509Certificate", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
- m_xDocumentHandler->characters(m_rInformation.ouX509Certificate);
- m_xDocumentHandler->endElement("X509Certificate");
- m_xDocumentHandler->endElement("X509Data");
+ assert(m_rInformation.GetSigningCertificate());
+ for (auto const& rData : m_rInformation.X509Datas)
+ {
+ m_xDocumentHandler->startElement(
+ "X509Data", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ for (auto const& it : rData)
+ {
+ m_xDocumentHandler->startElement(
+ "X509Certificate",
+ uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters(it.X509Certificate);
+ m_xDocumentHandler->endElement("X509Certificate");
+ }
+ m_xDocumentHandler->endElement("X509Data");
+ }
m_xDocumentHandler->endElement("KeyInfo");
}
diff --git a/xmlsecurity/source/helper/ooxmlsecparser.cxx b/xmlsecurity/source/helper/ooxmlsecparser.cxx
index c22e8c2261bf..42f226f57d14 100644
--- a/xmlsecurity/source/helper/ooxmlsecparser.cxx
+++ b/xmlsecurity/source/helper/ooxmlsecparser.cxx
@@ -11,32 +11,1241 @@
#include "ooxmlsecparser.hxx"
#include <xmlsignaturehelper.hxx>
#include <xsecctl.hxx>
+
+#include <xmloff/xmlnmspe.hxx>
+#include <xmloff/xmlimp.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+
#include <sal/log.hxx>
using namespace com::sun::star;
+class OOXMLSecParser::Context
+{
+ protected:
+ friend class OOXMLSecParser;
+ OOXMLSecParser & m_rParser;
+ private:
+ std::unique_ptr<SvXMLNamespaceMap> m_pOldNamespaceMap;
+
+ public:
+ Context(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : m_rParser(rParser)
+ , m_pOldNamespaceMap(std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual ~Context() = default;
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/)
+ {
+ }
+
+ virtual void EndElement()
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/);
+
+ virtual void Characters(OUString const& /*rChars*/)
+ {
+ }
+};
+
+// it's possible that an unsupported element has an Id attribute and a
+// ds:Reference digesting it - probably this means XSecController needs to know
+// about it. (For known elements, the Id attribute is only processed according
+// to the schema.)
+class OOXMLSecParser::UnknownContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ UnknownContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+};
+
+auto OOXMLSecParser::Context::CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/)
+-> std::unique_ptr<Context>
+{
+ // default: create new base context
+ return std::make_unique<UnknownContext>(m_rParser, std::move(pOldNamespaceMap));
+}
+
+/**
+note: anything in ds:Object should be trusted *only* if there is a ds:Reference
+ to it so it is signed (exception: the xades:EncapsulatedX509Certificate).
+ ds:SignedInfo precedes all ds:Object.
+
+ There may be multiple ds:Signature for purpose of counter-signatures
+ but the way XAdES describes these, only the ds:SignatureValue element
+ would be referenced, so requiring a ds:Reference for anything in
+ ds:Object shouldn't cause issues.
+ */
+class OOXMLSecParser::ReferencedContextImpl
+ : public OOXMLSecParser::Context
+{
+ protected:
+ bool m_isReferenced;
+
+ public:
+ ReferencedContextImpl(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_isReferenced(isReferenced)
+ {
+ }
+
+ OUString CheckIdAttrReferenced(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+ {
+ OUString const id(m_rParser.HandleIdAttr(xAttrs));
+ if (!id.isEmpty() && m_rParser.m_pXSecController->haveReferenceForId(id))
+ {
+ m_isReferenced = true;
+ }
+ return id;
+ }
+};
+
+class OOXMLSecParser::DsX509CertificateContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509CertificateContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsX509SerialNumberContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509SerialNumberContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsX509IssuerNameContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509IssuerNameContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsX509IssuerSerialContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rX509IssuerName;
+ OUString & m_rX509SerialNumber;
+
+ public:
+ DsX509IssuerSerialContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rIssuerName, OUString & rSerialNumber)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rX509IssuerName(rIssuerName)
+ , m_rX509SerialNumber(rSerialNumber)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName")
+ {
+ return std::make_unique<DsX509IssuerNameContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509IssuerName);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber")
+ {
+ return std::make_unique<DsX509SerialNumberContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509SerialNumber);
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+/// can't be sure what is supposed to happen here because the spec is clear as mud
+class OOXMLSecParser::DsX509DataContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ // sigh... "No ordering is implied by the above constraints."
+ // so store the ball of mud in vectors and try to figure it out later.
+ std::vector<std::pair<OUString, OUString>> m_X509IssuerSerials;
+ std::vector<OUString> m_X509Certificates;
+
+ public:
+ DsX509DataContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setX509Data(m_X509IssuerSerials, m_X509Certificates);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial")
+ {
+ m_X509IssuerSerials.emplace_back();
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerSerials.back().first, m_X509IssuerSerials.back().second);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate")
+ {
+ m_X509Certificates.emplace_back();
+ return std::make_unique<DsX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_X509Certificates.back());
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsKeyInfoContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsKeyInfoContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Data")
+ {
+ return std::make_unique<DsX509DataContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:PGPData
+ // missing: ds:KeyName, ds:KeyValue, ds:RetrievalMethod, ds:SPKIData, ds:MgmtData
+ // (old code would read ds:Transform inside ds:RetrievalMethod but
+ // presumably that was a bug)
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+
+};
+
+class OOXMLSecParser::DsSignatureValueContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsSignatureValueContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setSignatureValue(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class OOXMLSecParser::DsDigestValueContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsDigestValueContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/) override
+ {
+ m_rValue.clear();
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsDigestMethodContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ DsDigestMethodContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_Int32 & rReferenceDigestID)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ SAL_WARN_IF( ouAlgorithm.isEmpty(), "xmlsecurity.helper", "no Algorithm in Reference" );
+ if (!ouAlgorithm.isEmpty())
+ {
+ SAL_WARN_IF( ouAlgorithm != ALGO_XMLDSIGSHA1
+ && ouAlgorithm != ALGO_XMLDSIGSHA256
+ && ouAlgorithm != ALGO_XMLDSIGSHA512,
+ "xmlsecurity.helper", "Algorithm neither SHA1, SHA256 nor SHA512");
+ if (ouAlgorithm == ALGO_XMLDSIGSHA1)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA256)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA256;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA512)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA512;
+ else
+ m_rReferenceDigestID = 0;
+ }
+ }
+};
+
+class OOXMLSecParser::DsTransformContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool & rIsC14N)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString aAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ if (aAlgorithm == ALGO_RELATIONSHIP)
+ {
+ m_rIsC14N = true;
+ }
+ }
+};
+
+class OOXMLSecParser::DsTransformsContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformsContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool & rIsC14N)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transform")
+ {
+ return std::make_unique<DsTransformContext>(m_rParser, std::move(pOldNamespaceMap), m_rIsC14N);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsReferenceContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString m_URI;
+ OUString m_Type;
+ OUString m_DigestValue;
+ bool m_IsC14N = false;
+ // Relevant for ODF. The digest algorithm selected by the DigestMethod
+ // element's Algorithm attribute. @see css::xml::crypto::DigestID.
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA256;
+
+ public:
+ DsReferenceContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+
+ m_URI = xAttrs->getValueByName("URI");
+ SAL_WARN_IF(m_URI.isEmpty(), "xmlsecurity.helper", "URI is empty");
+ // Remember the type of this reference.
+ m_Type = xAttrs->getValueByName("Type");
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_URI.startsWith("#"))
+ {
+ /*
+ * remove the first character '#' from the attribute value
+ */
+ m_rParser.m_pXSecController->addReference(m_URI.copy(1), m_nReferenceDigestID, m_Type);
+ }
+ else
+ {
+ if (m_IsC14N) // this is determined by nested ds:Transform
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, false, m_nReferenceDigestID);
+ }
+ else
+ /*
+ * it must be an octet stream
+ */
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, true, m_nReferenceDigestID);
+ }
+ }
+
+ m_rParser.m_pXSecController->setDigestValue(m_nReferenceDigestID, m_DigestValue);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transforms")
+ {
+ return std::make_unique<DsTransformsContext>(m_rParser, std::move(pOldNamespaceMap), m_IsC14N);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_DigestValue);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsSignatureMethodContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsSignatureMethodContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+ if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256
+ || ouAlgorithm == ALGO_ECDSASHA512)
+ {
+ m_rParser.m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA);
+ }
+ }
+};
+
+class OOXMLSecParser::DsSignedInfoContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsSignedInfoContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setReferenceCount();
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureMethod")
+ {
+ return std::make_unique<DsSignatureMethodContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference")
+ {
+ return std::make_unique<DsReferenceContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:CanonicalizationMethod
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesCertDigestContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rDigestValue;
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ XadesCertDigestContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rDigestValue, sal_Int32 & rReferenceDigestID)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rDigestValue(rDigestValue)
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_rReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rDigestValue);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesCertContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ OUString m_CertDigest;
+ OUString m_X509IssuerName;
+ OUString m_X509SerialNumber;
+
+ public:
+ XadesCertContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setX509CertDigest(m_CertDigest, m_nReferenceDigestID, m_X509IssuerName, m_X509SerialNumber);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned xades:Cert");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest")
+ {
+ return std::make_unique<XadesCertDigestContext>(m_rParser, std::move(pOldNamespaceMap), m_CertDigest, m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial")
+ {
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerName, m_X509SerialNumber);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesSigningCertificateContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSigningCertificateContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "Cert")
+ {
+ return std::make_unique<XadesCertContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesSigningTimeContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ XadesSigningTimeContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setDate("", m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SigningTime");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class OOXMLSecParser::XadesSignedSignaturePropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedSignaturePropertiesContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningTime")
+ {
+ return std::make_unique<XadesSigningTimeContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningCertificate")
+ {
+ return std::make_unique<XadesSigningCertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignaturePolicyIdentifier, xades:SignatureProductionPlace, xades:SignerRole
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesSignedPropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedPropertiesContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedSignatureProperties")
+ {
+ return std::make_unique<XadesSignedSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignedDataObjectProperties
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesQualifyingPropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesQualifyingPropertiesContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedProperties")
+ {
+ return std::make_unique<XadesSignedPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:UnsignedSignatureProperties
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::MsodigsigSetupIDContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MsodigsigSetupIDContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::MsodigsigSignatureCommentsContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MsodigsigSignatureCommentsContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::MsodigsigSignatureInfoV1Context
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_SetupID;
+ OUString m_SignatureComments;
+
+ public:
+ MsodigsigSignatureInfoV1Context(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SetupID")
+ {
+ return std::make_unique<MsodigsigSetupIDContext>(m_rParser, std::move(pOldNamespaceMap), m_SetupID);
+ }
+ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SignatureComments")
+ {
+ return std::make_unique<MsodigsigSignatureCommentsContext>(m_rParser, std::move(pOldNamespaceMap), m_SignatureComments);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ if (!m_SetupID.isEmpty())
+ {
+ m_rParser.m_pXSecController->setSignatureLineId(m_SetupID);
+ }
+ if (!m_SignatureComments.isEmpty())
+ {
+ m_rParser.m_pXSecController->setDescription("", m_SignatureComments);
+
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureInfoV1");
+ }
+ }
+};
+
+class OOXMLSecParser::MdssiValueContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MdssiValueContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::MdssiSignatureTimeContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MdssiSignatureTimeContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_MDSSI && rName == "Value")
+ {
+ return std::make_unique<MdssiValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rValue);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+
+class OOXMLSecParser::DsSignaturePropertyContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ enum class SignatureProperty { Unknown, Date, Info };
+ SignatureProperty m_Property = SignatureProperty::Unknown;
+ OUString m_Id;
+ OUString m_Value;
+
+ public:
+ DsSignaturePropertyContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_Id = CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ switch (m_Property)
+ {
+ case SignatureProperty::Unknown:
+ SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored");
+ break;
+ case SignatureProperty::Info:
+ break; // handled by child context
+ case SignatureProperty::Date:
+ m_rParser.m_pXSecController->setDate(m_Id, m_Value);
+ break;
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureProperty");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_MDSSI && rName == "SignatureTime")
+ {
+ m_Property = SignatureProperty::Date;
+ return std::make_unique<MdssiSignatureTimeContext>(m_rParser, std::move(pOldNamespaceMap), m_Value);
+ }
+ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SignatureInfoV1")
+ {
+ return std::make_unique<MsodigsigSignatureInfoV1Context>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsSignaturePropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ DsSignaturePropertiesContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperty")
+ {
+ return std::make_unique<DsSignaturePropertyContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsManifestContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ DsManifestContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+#if 0
+ ???
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setReferenceCount();
+ }
+#endif
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference")
+ {
+ return std::make_unique<DsReferenceContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:CanonicalizationMethod
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsObjectContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ enum class Mode { Default, ValidSignatureLineImage, InvalidSignatureLineImage };
+ Mode m_Mode = Mode::Default;
+ OUString m_Value;
+
+ public:
+ DsObjectContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ // init with "false" here - the Signature element can't be referenced by its child
+ : OOXMLSecParser::ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), false)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString const id(CheckIdAttrReferenced(xAttrs));
+ if (id == "idValidSigLnImg")
+ {
+ m_Mode = Mode::ValidSignatureLineImage;
+ }
+ else if (id == "idInvalidSigLnImg")
+ {
+ m_Mode = Mode::InvalidSignatureLineImage;
+ }
+ }
+
+ virtual void EndElement() override
+ {
+ switch (m_Mode)
+ {
+ case Mode::ValidSignatureLineImage:
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setValidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineValidImage");
+ }
+ break;
+ case Mode::InvalidSignatureLineImage:
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineInvalidImage");
+ }
+ break;
+ case Mode::Default:
+ break;
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperties")
+ {
+ return std::make_unique<DsSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "QualifyingProperties")
+ {
+ return std::make_unique<XadesQualifyingPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Manifest")
+ {
+ return std::make_unique<DsManifestContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsSignatureContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsSignatureContext(OOXMLSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs));
+ m_rParser.m_rXMLSignatureHelper.StartVerifySignatureElement();
+ m_rParser.m_pXSecController->addSignature();
+ if (!ouIdAttr.isEmpty())
+ {
+ m_rParser.m_pXSecController->setId( ouIdAttr );
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignedInfo")
+ {
+ return std::make_unique<DsSignedInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureValue")
+ {
+ return std::make_unique<DsSignatureValueContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "KeyInfo")
+ {
+ return std::make_unique<DsKeyInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Object")
+ {
+ return std::make_unique<DsObjectContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+
OOXMLSecParser::OOXMLSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController)
- : m_pXSecController(pXSecController)
- ,m_bInDigestValue(false)
- ,m_bInSignatureValue(false)
- ,m_bInX509Certificate(false)
- ,m_bInMdssiValue(false)
- ,m_bInSignatureComments(false)
- ,m_bInX509IssuerName(false)
- ,m_bInX509SerialNumber(false)
- ,m_bInCertDigest(false)
- ,m_bInValidSignatureImage(false)
- ,m_bInInvalidSignatureImage(false)
- ,m_bInSignatureLineId(false)
- ,m_bReferenceUnresolved(false)
+ : m_pNamespaceMap(new SvXMLNamespaceMap)
+ , m_pXSecController(pXSecController)
,m_rXMLSignatureHelper(rXMLSignatureHelper)
{
+ using namespace xmloff::token;
+ m_pNamespaceMap->Add( GetXMLToken(XML_XML), GetXMLToken(XML_N_XML), XML_NAMESPACE_XML );
+ m_pNamespaceMap->Add( "_ds", GetXMLToken(XML_N_DS), XML_NAMESPACE_DS );
+ m_pNamespaceMap->Add( "_xades132", GetXMLToken(XML_N_XADES132), XML_NAMESPACE_XADES132);
+ m_pNamespaceMap->Add( "_xades141", GetXMLToken(XML_N_XADES141), XML_NAMESPACE_XADES141);
+ m_pNamespaceMap->Add( "_dc", GetXMLToken(XML_N_DC), XML_NAMESPACE_DC );
+ m_pNamespaceMap->Add( "_mdssi", NS_MDSSI, XML_NAMESPACE_MDSSI );
+ m_pNamespaceMap->Add( "_msodigsig", "http://schemas.microsoft.com/office/2006/digsig", XML_NAMESPACE_MSODIGSIG );
+ m_pNamespaceMap->Add( "_office_libo",
+ GetXMLToken(XML_N_LO_EXT), XML_NAMESPACE_LO_EXT);
}
OOXMLSecParser::~OOXMLSecParser()
{
}
+OUString OOXMLSecParser::HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+{
+ OUString const aId = xAttrs->getValueByName("Id");
+ if (!aId.isEmpty())
+ {
+ m_pXSecController->collectToVerify(aId);
+ }
+ return aId;
+}
+
void SAL_CALL OOXMLSecParser::startDocument()
{
if (m_xNextHandler.is())
@@ -51,217 +1260,69 @@ void SAL_CALL OOXMLSecParser::endDocument()
void SAL_CALL OOXMLSecParser::startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs)
{
- OUString aId = xAttribs->getValueByName("Id");
- if (!aId.isEmpty())
- m_pXSecController->collectToVerify(aId);
+ assert(m_pNamespaceMap);
+ std::unique_ptr<SvXMLNamespaceMap> pRewindMap(
+ SvXMLImport::processNSAttributes(m_pNamespaceMap, nullptr, xAttribs));
- if (rName == "Signature")
- {
- m_rXMLSignatureHelper.StartVerifySignatureElement();
- m_pXSecController->addSignature();
- if (!aId.isEmpty())
- m_pXSecController->setId(aId);
- }
- else if (rName == "SignatureMethod")
- {
- OUString ouAlgorithm = xAttribs->getValueByName("Algorithm");
- if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256
- || ouAlgorithm == ALGO_ECDSASHA512)
- m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA);
- }
- else if (rName == "Reference")
- {
- OUString aURI = xAttribs->getValueByName("URI");
- if (aURI.startsWith("#"))
- m_pXSecController->addReference(aURI.copy(1), xml::crypto::DigestID::SHA1, OUString());
- else
- {
- m_aReferenceURI = aURI;
- m_bReferenceUnresolved = true;
- }
- }
- else if (rName == "Transform")
- {
- if (m_bReferenceUnresolved)
- {
- OUString aAlgorithm = xAttribs->getValueByName("Algorithm");
- if (aAlgorithm == ALGO_RELATIONSHIP)
- {
- m_pXSecController->addStreamReference(m_aReferenceURI, /*isBinary=*/false, /*nDigestID=*/xml::crypto::DigestID::SHA256);
- m_bReferenceUnresolved = false;
- }
- }
- }
- else if (rName == "DigestValue" && !m_bInCertDigest)
- {
- m_aDigestValue.clear();
- m_bInDigestValue = true;
- }
- else if (rName == "SignatureValue")
- {
- m_aSignatureValue.clear();
- m_bInSignatureValue = true;
- }
- else if (rName == "X509Certificate")
- {
- m_aX509Certificate.clear();
- m_bInX509Certificate = true;
- }
- else if (rName == "mdssi:Value")
- {
- m_aMdssiValue.clear();
- m_bInMdssiValue = true;
- }
- else if (rName == "SignatureComments")
- {
- m_aSignatureComments.clear();
- m_bInSignatureComments = true;
- }
- else if (rName == "X509IssuerName")
- {
- m_aX509IssuerName.clear();
- m_bInX509IssuerName = true;
- }
- else if (rName == "X509SerialNumber")
- {
- m_aX509SerialNumber.clear();
- m_bInX509SerialNumber = true;
- }
- else if (rName == "xd:CertDigest")
- {
- m_aCertDigest.clear();
- m_bInCertDigest = true;
- }
- else if (rName == "Object")
+ OUString localName;
+ sal_uInt16 const nPrefix(m_pNamespaceMap->GetKeyByAttrName(rName, &localName));
+
+ std::unique_ptr<Context> pContext;
+
+ if (m_ContextStack.empty())
{
- OUString sId = xAttribs->getValueByName("Id");
- if (sId == "idValidSigLnImg")
- {
- m_aValidSignatureImage.clear();
- m_bInValidSignatureImage = true;
- }
- else if (sId == "idInvalidSigLnImg")
+ if (nPrefix == XML_NAMESPACE_DS
+ && localName == "Signature")
{
- m_aInvalidSignatureImage.clear();
- m_bInInvalidSignatureImage = true;
+ pContext.reset(new DsSignatureContext(*this, std::move(pRewindMap)));
}
else
{
- SAL_INFO("xmlsecurity.ooxml", "Unknown 'Object' child element: " << rName);
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected root element", nullptr,
+ css::uno::Any());
}
}
- else if (rName == "SetupID")
- {
- m_aSignatureLineId.clear();
- m_bInSignatureLineId = true;
- }
else
{
- SAL_INFO("xmlsecurity.ooxml", "Unknown xml element: " << rName);
+ pContext = m_ContextStack.top()->CreateChildContext(
+ std::move(pRewindMap), nPrefix, localName);
}
+ m_ContextStack.push(std::move(pContext));
+ assert(!pRewindMap);
+
+ m_ContextStack.top()->StartElement(xAttribs);
+
if (m_xNextHandler.is())
+ {
m_xNextHandler->startElement(rName, xAttribs);
+ }
+
}
void SAL_CALL OOXMLSecParser::endElement(const OUString& rName)
{
- if (rName == "SignedInfo")
- m_pXSecController->setReferenceCount();
- else if (rName == "Reference")
- {
- if (m_bReferenceUnresolved)
- {
- // No transform algorithm found, assume binary.
- m_pXSecController->addStreamReference(m_aReferenceURI, /*isBinary=*/true, /*nDigestID=*/xml::crypto::DigestID::SHA256);
- m_bReferenceUnresolved = false;
- }
- m_pXSecController->setDigestValue(xml::crypto::DigestID::SHA256, m_aDigestValue);
- }
- else if (rName == "DigestValue" && !m_bInCertDigest)
- m_bInDigestValue = false;
- else if (rName == "SignatureValue")
- {
- m_pXSecController->setSignatureValue(m_aSignatureValue);
- m_bInSignatureValue = false;
- }
- else if (rName == "X509Certificate")
- {
- m_pXSecController->setX509Certificate(m_aX509Certificate);
- m_bInX509Certificate = false;
- }
- else if (rName == "mdssi:Value")
- {
- m_pXSecController->setDate(m_aMdssiValue);
- m_bInMdssiValue = false;
- }
- else if (rName == "SignatureComments")
- {
- m_pXSecController->setDescription(m_aSignatureComments);
- m_bInSignatureComments = false;
- }
- else if (rName == "X509IssuerName")
- {
- m_pXSecController->setX509IssuerName(m_aX509IssuerName);
- m_bInX509IssuerName = false;
- }
- else if (rName == "X509SerialNumber")
- {
- m_pXSecController->setX509SerialNumber(m_aX509SerialNumber);
- m_bInX509SerialNumber = false;
- }
- else if (rName == "xd:CertDigest")
- {
- m_pXSecController->setCertDigest(m_aCertDigest);
- m_bInCertDigest = false;
- }
- else if (rName == "Object")
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+
+ m_ContextStack.top()->EndElement();
+
+ if (m_xNextHandler.is())
{
- if (m_bInValidSignatureImage)
- {
- m_pXSecController->setValidSignatureImage(m_aValidSignatureImage);
- m_bInValidSignatureImage = false;
- }
- else if (m_bInInvalidSignatureImage)
- {
- m_pXSecController->setInvalidSignatureImage(m_aInvalidSignatureImage);
- m_bInInvalidSignatureImage = false;
- }
+ m_xNextHandler->endElement(rName);
}
- else if (rName == "SetupID")
+
+ if (m_ContextStack.top()->m_pOldNamespaceMap)
{
- m_pXSecController->setSignatureLineId(m_aSignatureLineId);
- m_bInSignatureLineId = false;
+ m_pNamespaceMap = std::move(m_ContextStack.top()->m_pOldNamespaceMap);
}
-
- if (m_xNextHandler.is())
- m_xNextHandler->endElement(rName);
+ m_ContextStack.pop();
}
void SAL_CALL OOXMLSecParser::characters(const OUString& rChars)
{
- if (m_bInDigestValue && !m_bInCertDigest)
- m_aDigestValue += rChars;
- else if (m_bInSignatureValue)
- m_aSignatureValue += rChars;
- else if (m_bInX509Certificate)
- m_aX509Certificate += rChars;
- else if (m_bInMdssiValue)
- m_aMdssiValue += rChars;
- else if (m_bInSignatureComments)
- m_aSignatureComments += rChars;
- else if (m_bInX509IssuerName)
- m_aX509IssuerName += rChars;
- else if (m_bInX509SerialNumber)
- m_aX509SerialNumber += rChars;
- else if (m_bInCertDigest)
- m_aCertDigest += rChars;
- else if (m_bInValidSignatureImage)
- m_aValidSignatureImage += rChars;
- else if (m_bInInvalidSignatureImage)
- m_aInvalidSignatureImage += rChars;
- else if (m_bInSignatureLineId)
- m_aSignatureLineId += rChars;
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+ m_ContextStack.top()->Characters(rChars);
if (m_xNextHandler.is())
m_xNextHandler->characters(rChars);
diff --git a/xmlsecurity/source/helper/ooxmlsecparser.hxx b/xmlsecurity/source/helper/ooxmlsecparser.hxx
index d3c199147255..540028b22fc9 100644
--- a/xmlsecurity/source/helper/ooxmlsecparser.hxx
+++ b/xmlsecurity/source/helper/ooxmlsecparser.hxx
@@ -15,6 +15,10 @@
#include <cppuhelper/implbase.hxx>
+#include <xmloff/nmspmap.hxx>
+
+#include <stack>
+
class XSecController;
class XMLSignatureHelper;
@@ -25,38 +29,62 @@ class OOXMLSecParser: public cppu::WeakImplHelper
css::lang::XInitialization
>
{
+public:
+ class Context;
+private:
+ class UnknownContext;
+ class ReferencedContextImpl;
+ class DsX509CertificateContext;
+ class DsX509SerialNumberContext;
+ class DsX509IssuerNameContext;
+ class DsX509IssuerSerialContext;
+ class DsX509DataContext;
+ class DsKeyInfoContext;
+ class DsSignatureValueContext;
+ class DsDigestValueContext;
+ class DsDigestMethodContext;
+ class DsTransformContext;
+ class DsTransformsContext;
+ class DsReferenceContext;
+ class DsSignatureMethodContext;
+ class DsSignedInfoContext;
+ class XadesEncapsulatedX509CertificateContext;
+ class XadesCertificateValuesContext;
+ class XadesUnsignedSignaturePropertiesContext;
+ class XadesUnsignedPropertiesContext;
+ class XadesCertDigestContext;
+ class XadesCertContext;
+ class XadesSigningCertificateContext;
+ class XadesSigningTimeContext;
+ class XadesSignedSignaturePropertiesContext;
+ class XadesSignedPropertiesContext;
+ class XadesQualifyingPropertiesContext;
+ class MdssiValueContext;
+ class MdssiSignatureTimeContext;
+ class MsodigsigSetupIDContext;
+ class MsodigsigSignatureCommentsContext;
+ class MsodigsigSignatureInfoV1Context;
+ class DsSignaturePropertyContext;
+ class DsSignaturePropertiesContext;
+ class DsManifestContext;
+ class DsObjectContext;
+ class DsSignatureContext;
+ class DsigSignaturesContext;
+
+ std::stack<std::unique_ptr<Context>> m_ContextStack;
+ std::unique_ptr<SvXMLNamespaceMap> m_pNamespaceMap;
+
XSecController* m_pXSecController;
css::uno::Reference<css::xml::sax::XDocumentHandler> m_xNextHandler;
- bool m_bInDigestValue;
- OUString m_aDigestValue;
- bool m_bInSignatureValue;
- OUString m_aSignatureValue;
- bool m_bInX509Certificate;
- OUString m_aX509Certificate;
- bool m_bInMdssiValue;
- OUString m_aMdssiValue;
- bool m_bInSignatureComments;
- OUString m_aSignatureComments;
- bool m_bInX509IssuerName;
- OUString m_aX509IssuerName;
- bool m_bInX509SerialNumber;
- OUString m_aX509SerialNumber;
- bool m_bInCertDigest;
- OUString m_aCertDigest;
- bool m_bInValidSignatureImage;
- OUString m_aValidSignatureImage;
- bool m_bInInvalidSignatureImage;
- OUString m_aInvalidSignatureImage;
- bool m_bInSignatureLineId;
- OUString m_aSignatureLineId;
-
/// Last seen <Reference URI="...">.
OUString m_aReferenceURI;
/// Already called addStreamReference() for this reference.
bool m_bReferenceUnresolved;
XMLSignatureHelper& m_rXMLSignatureHelper;
+ OUString HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs);
+
public:
explicit OOXMLSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController);
virtual ~OOXMLSecParser() override;
diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
index f10f29c61840..8a90b73901db 100644
--- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx
+++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
@@ -82,8 +82,12 @@ PDFSignatureHelper::GetDocumentSignatureInformations(
security::DocumentSignatureInformation& rExternal = aRet[i];
rExternal.SignatureIsValid
= rInternal.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
- if (!rInternal.ouX509Certificate.isEmpty())
- rExternal.Signer = xSecEnv->createCertificateFromAscii(rInternal.ouX509Certificate);
+ if (rInternal.GetSigningCertificate()
+ && !rInternal.GetSigningCertificate()->X509Certificate.isEmpty())
+ {
+ rExternal.Signer = xSecEnv->createCertificateFromAscii(
+ rInternal.GetSigningCertificate()->X509Certificate);
+ }
rExternal.PartialDocumentSignature = rInternal.bPartialDocumentSignature;
// Verify certificate.
diff --git a/xmlsecurity/source/helper/xmlsignaturehelper.cxx b/xmlsecurity/source/helper/xmlsignaturehelper.cxx
index 6ec834053a17..bcb79039e342 100644
--- a/xmlsecurity/source/helper/xmlsignaturehelper.cxx
+++ b/xmlsecurity/source/helper/xmlsignaturehelper.cxx
@@ -21,6 +21,7 @@
#include <xmlsignaturehelper.hxx>
#include <documentsignaturehelper.hxx>
#include <xsecctl.hxx>
+#include <biginteger.hxx>
#include <xmlsignaturehelper2.hxx>
@@ -45,6 +46,8 @@
#include <tools/diagnose_ex.h>
#include <sal/log.hxx>
+#include <optional>
+
#define NS_DOCUMENTSIGNATURES "http://openoffice.org/2004/documentsignatures"
#define NS_DOCUMENTSIGNATURES_ODF_1_2 "urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0"
#define OOXML_SIGNATURE_ORIGIN "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin"
@@ -402,6 +405,7 @@ bool XMLSignatureHelper::ReadAndVerifySignatureStorageStream(const css::uno::Ref
catch(const uno::Exception&)
{
DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper");
+ mbError = true;
}
mpXSecController->releaseSignatureReader();
@@ -546,4 +550,162 @@ void XMLSignatureHelper::CreateAndWriteOOXMLSignature(const uno::Reference<embed
xSaxWriter->endDocument();
}
+/** check this constraint from xmldsig-core 4.5.4:
+
+ All certificates appearing in an X509Data element MUST relate to the
+ validation key by either containing it or being part of a certification
+ chain that terminates in a certificate containing the validation key.
+ */
+static auto CheckX509Data(
+ uno::Reference<xml::crypto::XSecurityEnvironment> const& xSecEnv,
+ std::vector<SignatureInformation::X509CertInfo> const& rX509CertInfos,
+ std::vector<uno::Reference<security::XCertificate>> & rCerts,
+ std::vector<SignatureInformation::X509CertInfo> & rSorted) -> bool
+{
+ assert(rCerts.empty());
+ assert(rSorted.empty());
+ if (rX509CertInfos.empty())
+ {
+ SAL_WARN("xmlsecurity.comp", "no X509Data");
+ return false;
+ }
+ std::vector<uno::Reference<security::XCertificate>> certs;
+ for (SignatureInformation::X509CertInfo const& it : rX509CertInfos)
+ {
+ if (!it.X509Certificate.isEmpty())
+ {
+ certs.emplace_back(xSecEnv->createCertificateFromAscii(it.X509Certificate));
+ }
+ else
+ {
+ certs.emplace_back(xSecEnv->getCertificate(
+ it.X509IssuerName,
+ xmlsecurity::numericStringToBigInteger(it.X509SerialNumber)));
+ }
+ if (!certs.back().is())
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data cannot be parsed");
+ return false;
+ }
+ }
+
+ // first, search one whose issuer isn't in the list, or a self-signed one
+ std::optional<size_t> start;
+ for (size_t i = 0; i < certs.size(); ++i)
+ {
+ for (size_t j = 0; ; ++j)
+ {
+ if (j == certs.size())
+ {
+ if (start)
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate has no issuer but already have start of chain: " << certs[i]->getSubjectName());
+ return false;
+ }
+ start = i; // issuer isn't in the list
+ break;
+ }
+ if (xmlsecurity::EqualDistinguishedNames(certs[i]->getIssuerName(), certs[j]->getSubjectName()))
+ {
+ if (i == j) // self signed
+ {
+ if (start)
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate is self-signed but already have start of chain: " << certs[i]->getSubjectName());
+ return false;
+ }
+ start = i;
+ }
+ break;
+ }
+ }
+ }
+ std::vector<size_t> chain;
+ if (!start)
+ {
+ // this can only be a cycle?
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: cycle detected");
+ return false;
+ }
+ chain.emplace_back(*start);
+
+ // second, check that there is a chain, no tree or cycle...
+ for (size_t i = 0; i < certs.size(); ++i)
+ {
+ assert(chain.size() == i + 1);
+ for (size_t j = 0; j < certs.size(); ++j)
+ {
+ if (chain[i] != j)
+ {
+ if (xmlsecurity::EqualDistinguishedNames(
+ certs[chain[i]]->getSubjectName(), certs[j]->getIssuerName()))
+ {
+ if (chain.size() != i + 1) // already found issuee?
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 2 others: " << certs[chain[i]]->getSubjectName());
+ return false;
+ }
+ chain.emplace_back(j);
+ }
+ }
+ }
+ if (i == certs.size() - 1)
+ { // last one: must be a leaf
+ if (chain.size() != i + 1)
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate in cycle: " << certs[chain[i]]->getSubjectName());
+ return false;
+ }
+ }
+ else if (chain.size() != i + 2)
+ { // not issuer of another?
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 0 others: " << certs[chain[i]]->getSubjectName());
+ return false;
+ }
+ }
+
+ // success
+ assert(chain.size() == rX509CertInfos.size());
+ for (auto const& it : chain)
+ {
+ rSorted.emplace_back(rX509CertInfos[it]);
+ rCerts.emplace_back(certs[it]);
+ }
+ return true;
+}
+
+std::vector<uno::Reference<security::XCertificate>>
+XMLSignatureHelper::CheckAndUpdateSignatureInformation(
+ uno::Reference<xml::crypto::XSecurityEnvironment> const& xSecEnv,
+ SignatureInformation const& rInfo)
+{
+ // if the check fails, it's not possible to determine which X509Data
+ // contained the signing certificate - the UI cannot display something
+ // useful in this case, so prevent anything misleading by clearing the
+ // X509Datas.
+
+ std::vector<uno::Reference<security::XCertificate>> certs;
+ std::vector<SignatureInformation::X509Data> datas;
+ // TODO: for now, just merge all X509Datas together for checking...
+ // (this will probably break round-trip of signature with multiple X509Data,
+ // no idea if that is a problem)
+ SignatureInformation::X509Data temp;
+ SignatureInformation::X509Data tempResult;
+ for (auto const& rData : rInfo.X509Datas)
+ {
+ for (auto const& it : rData)
+ {
+ temp.emplace_back(it);
+ }
+ }
+ if (CheckX509Data(xSecEnv, temp, certs, tempResult))
+ {
+ datas.emplace_back(tempResult);
+ }
+
+ // rInfo is a copy, update the original
+ mpXSecController->UpdateSignatureInformation(rInfo.nSecurityId, datas);
+ return certs;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecctl.cxx b/xmlsecurity/source/helper/xsecctl.cxx
index cac30006b6a7..6bd88e24f91e 100644
--- a/xmlsecurity/source/helper/xsecctl.cxx
+++ b/xmlsecurity/source/helper/xsecctl.cxx
@@ -734,7 +734,7 @@ void XSecController::exportSignature(
xDocumentHandler->startElement(
"PGPKeyID",
css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
- xDocumentHandler->characters( signatureInfo.ouCertDigest );
+ xDocumentHandler->characters(signatureInfo.ouGpgKeyID);
xDocumentHandler->endElement( "PGPKeyID" );
/* Write PGPKeyPacket element */
@@ -758,43 +758,50 @@ void XSecController::exportSignature(
}
else
{
- /* Write X509Data element */
- xDocumentHandler->startElement(
- "X509Data",
- css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ assert(signatureInfo.GetSigningCertificate());
+ for (auto const& rData : signatureInfo.X509Datas)
{
- /* Write X509IssuerSerial element */
+ /* Write X509Data element */
xDocumentHandler->startElement(
- "X509IssuerSerial",
+ "X509Data",
css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
{
- /* Write X509IssuerName element */
- xDocumentHandler->startElement(
- "X509IssuerName",
- css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
- xDocumentHandler->characters( signatureInfo.ouX509IssuerName );
- xDocumentHandler->endElement( "X509IssuerName" );
-
- /* Write X509SerialNumber element */
- xDocumentHandler->startElement(
- "X509SerialNumber",
- css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
- xDocumentHandler->characters( signatureInfo.ouX509SerialNumber );
- xDocumentHandler->endElement( "X509SerialNumber" );
- }
- xDocumentHandler->endElement( "X509IssuerSerial" );
-
- /* Write X509Certificate element */
- if (!signatureInfo.ouX509Certificate.isEmpty())
- {
- xDocumentHandler->startElement(
- "X509Certificate",
- css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
- xDocumentHandler->characters( signatureInfo.ouX509Certificate );
- xDocumentHandler->endElement( "X509Certificate" );
+ for (auto const& it : rData)
+ {
+ /* Write X509IssuerSerial element */
+ xDocumentHandler->startElement(
+ "X509IssuerSerial",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ {
+ /* Write X509IssuerName element */
+ xDocumentHandler->startElement(
+ "X509IssuerName",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters(it.X509IssuerName);
+ xDocumentHandler->endElement( "X509IssuerName" );
+
+ /* Write X509SerialNumber element */
+ xDocumentHandler->startElement(
+ "X509SerialNumber",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters(it.X509SerialNumber);
+ xDocumentHandler->endElement( "X509SerialNumber" );
+ }
+ xDocumentHandler->endElement( "X509IssuerSerial" );
+
+ /* Write X509Certificate element */
+ if (!it.X509Certificate.isEmpty())
+ {
+ xDocumentHandler->startElement(
+ "X509Certificate",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters(it.X509Certificate);
+ xDocumentHandler->endElement( "X509Certificate" );
+ }
+ }
}
+ xDocumentHandler->endElement( "X509Data" );
}
- xDocumentHandler->endElement( "X509Data" );
}
}
xDocumentHandler->endElement( "KeyInfo" );
@@ -815,7 +822,7 @@ void XSecController::exportSignature(
pAttributeList = new SvXMLAttributeList();
pAttributeList->AddAttribute(
"Id",
- signatureInfo.ouPropertyId);
+ signatureInfo.ouDateTimePropertyId);
pAttributeList->AddAttribute(
"Target",
"#" + signatureInfo.ouSignatureId);
@@ -913,6 +920,15 @@ void XSecController::exportOOXMLSignature(const uno::Reference<embed::XStorage>&
aExporter.writeSignature();
}
+void XSecController::UpdateSignatureInformation(sal_Int32 const nSecurityId,
+ std::vector<SignatureInformation::X509Data> const& rDatas)
+{
+ SignatureInformation aInf( 0 );
+ int const nIndex = findSignatureInfor(nSecurityId);
+ assert(nIndex != -1); // nothing should touch this between parsing and verify
+ m_vInternalSignatureInformations[nIndex].signatureInfor.X509Datas = rDatas;
+}
+
SignatureInformation XSecController::getSignatureInformation( sal_Int32 nSecurityId ) const
{
SignatureInformation aInf( 0 );
diff --git a/xmlsecurity/source/helper/xsecparser.cxx b/xmlsecurity/source/helper/xsecparser.cxx
index 9f2bbe074a1b..f2e00fca99c9 100644
--- a/xmlsecurity/source/helper/xsecparser.cxx
+++ b/xmlsecurity/source/helper/xsecparser.cxx
@@ -21,131 +21,474 @@
#include "xsecparser.hxx"
#include <xsecctl.hxx>
#include <xmlsignaturehelper.hxx>
+
+#include <xmloff/xmlnmspe.hxx>
+#include <xmloff/xmlimp.hxx>
+
#include <com/sun/star/xml/sax/SAXException.hpp>
#include <cppuhelper/exc_hlp.hxx>
#include <sal/log.hxx>
-
-XSecParser::XSecParser(XMLSignatureHelper& rXMLSignatureHelper,
- XSecController* pXSecController)
- : m_bInX509IssuerName(false)
- , m_bInX509SerialNumber(false)
- , m_bInX509Certificate(false)
- , m_bInGpgCertificate(false)
- , m_bInGpgKeyID(false)
- , m_bInGpgOwner(false)
- , m_bInCertDigest(false)
- , m_bInEncapsulatedX509Certificate(false)
- , m_bInSigningTime(false)
- , m_bInDigestValue(false)
- , m_bInSignatureValue(false)
- , m_bInDate(false)
- , m_bInDescription(false)
- , m_bInSignatureLineId(false)
- , m_bInSignatureLineValidImage(false)
- , m_bInSignatureLineInvalidImage(false)
- , m_pXSecController(pXSecController)
- , m_bReferenceUnresolved(false)
- , m_nReferenceDigestID(css::xml::crypto::DigestID::SHA1)
- , m_rXMLSignatureHelper(rXMLSignatureHelper)
+class XSecParser::Context
{
-}
+ protected:
+ friend class XSecParser;
+ XSecParser & m_rParser;
+ private:
+ std::unique_ptr<SvXMLNamespaceMap> m_pOldNamespaceMap;
-OUString XSecParser::getIdAttr(const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+ public:
+ Context(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : m_rParser(rParser)
+ , m_pOldNamespaceMap(std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual ~Context() = default;
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/)
+ {
+ }
+
+ virtual void EndElement()
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/);
+
+ virtual void Characters(OUString const& /*rChars*/)
+ {
+ }
+};
+
+// it's possible that an unsupported element has an Id attribute and a
+// ds:Reference digesting it - probably this means XSecController needs to know
+// about it. (For known elements, the Id attribute is only processed according
+// to the schema.)
+class XSecParser::UnknownContext
+ : public XSecParser::Context
{
- OUString ouIdAttr = xAttribs->getValueByName("id");
+ public:
+ UnknownContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
- if (ouIdAttr.isEmpty())
- {
- ouIdAttr = xAttribs->getValueByName("Id");
- }
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+};
- return ouIdAttr;
+auto XSecParser::Context::CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/)
+-> std::unique_ptr<Context>
+{
+ // default: create new base context
+ return std::make_unique<UnknownContext>(m_rParser, std::move(pOldNamespaceMap));
}
-/*
- * XDocumentHandler
+/**
+note: anything in ds:Object should be trusted *only* if there is a ds:Reference
+ to it so it is signed (exception: the xades:EncapsulatedX509Certificate).
+ ds:SignedInfo precedes all ds:Object.
+
+ There may be multiple ds:Signature for purpose of counter-signatures
+ but the way XAdES describes these, only the ds:SignatureValue element
+ would be referenced, so requiring a ds:Reference for anything in
+ ds:Object shouldn't cause issues.
*/
-void SAL_CALL XSecParser::startDocument( )
+class XSecParser::ReferencedContextImpl
+ : public XSecParser::Context
{
- m_bInX509IssuerName = false;
- m_bInX509SerialNumber = false;
- m_bInX509Certificate = false;
- m_bInGpgCertificate = false;
- m_bInGpgKeyID = false;
- m_bInGpgOwner = false;
- m_bInSignatureValue = false;
- m_bInDigestValue = false;
- m_bInDate = false;
- m_bInDescription = false;
+ protected:
+ bool m_isReferenced;
- if (m_xNextHandler.is())
- {
- m_xNextHandler->startDocument();
- }
-}
+ public:
+ ReferencedContextImpl(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_isReferenced(isReferenced)
+ {
+ }
-void SAL_CALL XSecParser::endDocument( )
+ OUString CheckIdAttrReferenced(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+ {
+ OUString const id(m_rParser.HandleIdAttr(xAttrs));
+ if (!id.isEmpty() && m_rParser.m_pXSecController->haveReferenceForId(id))
+ {
+ m_isReferenced = true;
+ }
+ return id;
+ }
+};
+
+class XSecParser::LoPGPOwnerContext
+ : public XSecParser::Context
{
- if (m_xNextHandler.is())
- {
- m_xNextHandler->endDocument();
- }
-}
+ private:
+ OUString m_Value;
-void SAL_CALL XSecParser::startElement(
- const OUString& aName,
- const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+ public:
+ LoPGPOwnerContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setGpgOwner(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsPGPKeyPacketContext
+ : public XSecParser::Context
{
- try
- {
- OUString ouIdAttr = getIdAttr(xAttribs);
- if (!ouIdAttr.isEmpty())
+ private:
+ OUString m_Value;
+
+ public:
+ DsPGPKeyPacketContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
{
- m_pXSecController->collectToVerify( ouIdAttr );
}
- if ( aName == "Signature" )
+ virtual void EndElement() override
{
- m_rXMLSignatureHelper.StartVerifySignatureElement();
- m_pXSecController->addSignature();
- if (!ouIdAttr.isEmpty())
+ m_rParser.m_pXSecController->setGpgCertificate(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsPGPKeyIDContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsPGPKeyIDContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setGpgKeyID(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsPGPDataContext
+ : public XSecParser::Context
+{
+ public:
+ DsPGPDataContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/) override
+ {
+ m_rParser.m_pXSecController->switchGpgSignature();
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPKeyID")
+ {
+ return std::make_unique<DsPGPKeyIDContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPKeyPacket")
+ {
+ return std::make_unique<DsPGPKeyPacketContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "PGPOwner")
{
- m_pXSecController->setId( ouIdAttr );
+ return std::make_unique<LoPGPOwnerContext>(m_rParser, std::move(pOldNamespaceMap));
}
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "SignatureMethod")
+};
+
+class XSecParser::DsX509CertificateContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509CertificateContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
{
- OUString ouAlgorithm = xAttribs->getValueByName("Algorithm");
- if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256
- || ouAlgorithm == ALGO_ECDSASHA512)
- m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA);
}
- else if ( aName == "Reference" )
+
+ virtual void Characters(OUString const& rChars) override
{
- OUString ouUri = xAttribs->getValueByName("URI");
- SAL_WARN_IF( ouUri.isEmpty(), "xmlsecurity.helper", "URI is empty" );
- // Remember the type of this reference.
- OUString ouType = xAttribs->getValueByName("Type");
- if (ouUri.startsWith("#"))
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsX509SerialNumberContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509SerialNumberContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsX509IssuerNameContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509IssuerNameContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsX509IssuerSerialContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rX509IssuerName;
+ OUString & m_rX509SerialNumber;
+
+ public:
+ DsX509IssuerSerialContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rIssuerName, OUString & rSerialNumber)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rX509IssuerName(rIssuerName)
+ , m_rX509SerialNumber(rSerialNumber)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName")
{
- /*
- * remove the first character '#' from the attribute value
- */
- m_pXSecController->addReference( ouUri.copy(1), m_nReferenceDigestID, ouType );
+ return std::make_unique<DsX509IssuerNameContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509IssuerName);
}
- else
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber")
{
- /*
- * remember the uri
- */
- m_currentReferenceURI = ouUri;
- m_bReferenceUnresolved = true;
+ return std::make_unique<DsX509SerialNumberContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509SerialNumber);
}
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "DigestMethod")
+};
+
+/// can't be sure what is supposed to happen here because the spec is clear as mud
+class XSecParser::DsX509DataContext
+ : public XSecParser::Context
+{
+ private:
+ // sigh... "No ordering is implied by the above constraints."
+ // so store the ball of mud in vectors and try to figure it out later.
+ std::vector<std::pair<OUString, OUString>> m_X509IssuerSerials;
+ std::vector<OUString> m_X509Certificates;
+
+ public:
+ DsX509DataContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
{
- OUString ouAlgorithm = xAttribs->getValueByName("Algorithm");
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setX509Data(m_X509IssuerSerials, m_X509Certificates);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial")
+ {
+ m_X509IssuerSerials.emplace_back();
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerSerials.back().first, m_X509IssuerSerials.back().second);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate")
+ {
+ m_X509Certificates.emplace_back();
+ return std::make_unique<DsX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_X509Certificates.back());
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsKeyInfoContext
+ : public XSecParser::Context
+{
+ public:
+ DsKeyInfoContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Data")
+ {
+ return std::make_unique<DsX509DataContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPData")
+ {
+ return std::make_unique<DsPGPDataContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:KeyName, ds:KeyValue, ds:RetrievalMethod, ds:SPKIData, ds:MgmtData
+ // (old code would read ds:Transform inside ds:RetrievalMethod but
+ // presumably that was a bug)
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+
+};
+
+class XSecParser::DsSignatureValueContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsSignatureValueContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setSignatureValue(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsDigestValueContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsDigestValueContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/) override
+ {
+ m_rValue.clear();
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsDigestMethodContext
+ : public XSecParser::Context
+{
+ private:
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ DsDigestMethodContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_Int32 & rReferenceDigestID)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
SAL_WARN_IF( ouAlgorithm.isEmpty(), "xmlsecurity.helper", "no Algorithm in Reference" );
if (!ouAlgorithm.isEmpty())
@@ -155,342 +498,1102 @@ void SAL_CALL XSecParser::startElement(
&& ouAlgorithm != ALGO_XMLDSIGSHA512,
"xmlsecurity.helper", "Algorithm neither SHA1, SHA256 nor SHA512");
if (ouAlgorithm == ALGO_XMLDSIGSHA1)
- m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA1;
else if (ouAlgorithm == ALGO_XMLDSIGSHA256)
- m_nReferenceDigestID = css::xml::crypto::DigestID::SHA256;
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA256;
else if (ouAlgorithm == ALGO_XMLDSIGSHA512)
- m_nReferenceDigestID = css::xml::crypto::DigestID::SHA512;
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA512;
else
- m_nReferenceDigestID = 0;
+ m_rReferenceDigestID = 0;
}
}
- else if (aName == "Transform")
+};
+
+class XSecParser::DsTransformContext
+ : public XSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool & rIsC14N)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ if (ouAlgorithm == ALGO_C14N)
+ /*
+ * a xml stream
+ */
+ {
+ m_rIsC14N = true;
+ }
+ }
+};
+
+class XSecParser::DsTransformsContext
+ : public XSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformsContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool & rIsC14N)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
{
- if ( m_bReferenceUnresolved )
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transform")
{
- OUString ouAlgorithm = xAttribs->getValueByName("Algorithm");
+ return std::make_unique<DsTransformContext>(m_rParser, std::move(pOldNamespaceMap), m_rIsC14N);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
- if (ouAlgorithm == ALGO_C14N)
- /*
- * a xml stream
- */
+class XSecParser::DsReferenceContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_URI;
+ OUString m_Type;
+ OUString m_DigestValue;
+ bool m_IsC14N = false;
+ // Relevant for ODF. The digest algorithm selected by the DigestMethod
+ // element's Algorithm attribute. @see css::xml::crypto::DigestID.
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+
+ public:
+ DsReferenceContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+
+ m_URI = xAttrs->getValueByName("URI");
+ SAL_WARN_IF(m_URI.isEmpty(), "xmlsecurity.helper", "URI is empty");
+ // Remember the type of this reference.
+ m_Type = xAttrs->getValueByName("Type");
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_URI.startsWith("#"))
+ {
+ /*
+ * remove the first character '#' from the attribute value
+ */
+ m_rParser.m_pXSecController->addReference(m_URI.copy(1), m_nReferenceDigestID, m_Type);
+ }
+ else
+ {
+ if (m_IsC14N) // this is determined by nested ds:Transform
{
- m_pXSecController->addStreamReference( m_currentReferenceURI, false, m_nReferenceDigestID );
- m_bReferenceUnresolved = false;
+ m_rParser.m_pXSecController->addStreamReference(m_URI, false, m_nReferenceDigestID);
+ }
+ else
+ /*
+ * it must be an octet stream
+ */
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, true, m_nReferenceDigestID);
}
}
+
+ m_rParser.m_pXSecController->setDigestValue(m_nReferenceDigestID, m_DigestValue);
}
- else if (aName == "X509IssuerName")
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
{
- m_ouX509IssuerName.clear();
- m_bInX509IssuerName = true;
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transforms")
+ {
+ return std::make_unique<DsTransformsContext>(m_rParser, std::move(pOldNamespaceMap), m_IsC14N);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_DigestValue);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "X509SerialNumber")
+};
+
+class XSecParser::DsSignatureMethodContext
+ : public XSecParser::Context
+{
+ public:
+ DsSignatureMethodContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
{
- m_ouX509SerialNumber.clear();
- m_bInX509SerialNumber = true;
}
- else if (aName == "X509Certificate")
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
{
- m_ouX509Certificate.clear();
- m_bInX509Certificate = true;
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+ if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256
+ || ouAlgorithm == ALGO_ECDSASHA512)
+ {
+ m_rParser.m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA);
+ }
}
- else if (aName == "PGPData")
+};
+
+class XSecParser::DsSignedInfoContext
+ : public XSecParser::Context
+{
+ public:
+ DsSignedInfoContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
{
- m_pXSecController->switchGpgSignature();
}
- else if (aName == "PGPKeyID")
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
{
- m_ouGpgKeyID.clear();
- m_bInGpgKeyID = true;
+ m_rParser.HandleIdAttr(xAttrs);
}
- else if (aName == "PGPKeyPacket")
+
+ virtual void EndElement() override
{
- m_ouGpgCertificate.clear();
- m_bInGpgCertificate = true;
+ m_rParser.m_pXSecController->setReferenceCount();
}
- else if (aName == "loext:PGPOwner")
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
{
- m_ouGpgOwner.clear();
- m_bInGpgOwner = true;
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureMethod")
+ {
+ return std::make_unique<DsSignatureMethodContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference")
+ {
+ return std::make_unique<DsReferenceContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:CanonicalizationMethod
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "SignatureValue")
+};
+
+class XSecParser::XadesEncapsulatedX509CertificateContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ XadesEncapsulatedX509CertificateContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
{
- m_ouSignatureValue.clear();
- m_bInSignatureValue = true;
}
- else if (aName == "DigestValue" && !m_bInCertDigest)
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
{
- m_ouDigestValue.clear();
- m_bInDigestValue = true;
+ m_rParser.HandleIdAttr(xAttrs);
}
- else if (aName == "xd:CertDigest")
+
+ virtual void EndElement() override
{
- m_ouCertDigest.clear();
- m_bInCertDigest = true;
+ m_rParser.m_pXSecController->addEncapsulatedX509Certificate(m_Value);
}
- // FIXME: Existing code here in xmlsecurity uses "xd" as the namespace prefix for XAdES,
- // while the sample document attached to tdf#76142 uses "xades". So accept either here. Of
- // course this is idiotic and wrong, the right thing would be to use a proper way to parse
- // XML that would handle namespaces correctly. I have no idea how substantial re-plumbing of
- // this code that would require.
- else if (aName == "xd:EncapsulatedX509Certificate" || aName == "xades:EncapsulatedX509Certificate")
+
+ virtual void Characters(OUString const& rChars) override
{
- m_ouEncapsulatedX509Certificate.clear();
- m_bInEncapsulatedX509Certificate = true;
+ m_Value += rChars;
}
- else if (aName == "xd:SigningTime" || aName == "xades:SigningTime")
+};
+
+class XSecParser::XadesCertificateValuesContext
+ : public XSecParser::Context
+{
+ public:
+ XadesCertificateValuesContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
{
- m_ouDate.clear();
- m_bInSigningTime = true;
}
- else if ( aName == "SignatureProperty" )
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
{
- if (!ouIdAttr.isEmpty())
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "EncapsulatedX509Certificate")
{
- m_pXSecController->setPropertyId( ouIdAttr );
+ return std::make_unique<XadesEncapsulatedX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap));
}
+ // missing: xades:OtherCertificate
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "dc:date")
+};
+
+class XSecParser::XadesUnsignedSignaturePropertiesContext
+ : public XSecParser::Context
+{
+ public:
+ XadesUnsignedSignaturePropertiesContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
{
- if (m_ouDate.isEmpty())
- m_bInDate = true;
}
- else if (aName == "dc:description")
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
{
- m_ouDescription.clear();
- m_bInDescription = true;
+ m_rParser.HandleIdAttr(xAttrs);
}
- else if (aName == "loext:SignatureLineId")
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
{
- m_ouSignatureLineId.clear();
- m_bInSignatureLineId = true;
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertificateValues")
+ {
+ return std::make_unique<XadesCertificateValuesContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing:
+ // xades:CounterSignature
+ // ^ old code would read a ds:Signature inside it?
+ // xades:SignatureTimeStamp
+ // xades:CompleteCertificateRefs
+ // xades:CompleteRevocationRefs
+ // xades:AttributeCertificateRefs
+ // xades:AttributeRevocationRefs
+ // xades:SigAndRefsTimeStamp
+ // xades:RefsOnlyTimeStamp
+ // xades:RevocationValues
+ // xades:AttrAuthoritiesCertValues
+ // ^ old code: was equivalent to CertificateValues ???
+ // xades:AttributeRevocationValues
+ // xades:ArchiveTimeStamp
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "loext:SignatureLineValidImage")
+};
+
+class XSecParser::XadesUnsignedPropertiesContext
+ : public XSecParser::Context
+{
+ public:
+ XadesUnsignedPropertiesContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
{
- m_ouSignatureLineValidImage.clear();
- m_bInSignatureLineValidImage = true;
}
- else if (aName == "loext:SignatureLineInvalidImage")
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
{
- m_ouSignatureLineInvalidImage.clear();
- m_bInSignatureLineInvalidImage = true;
+ m_rParser.HandleIdAttr(xAttrs);
}
- if (m_xNextHandler.is())
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
{
- m_xNextHandler->startElement(aName, xAttribs);
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "UnsignedSignatureProperties")
+ {
+ return std::make_unique<XadesUnsignedSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: xades:UnsignedDataObjectProperties
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- }
- catch (css::uno::Exception& )
- {//getCaughtException MUST be the first line in the catch block
- css::uno::Any exc = cppu::getCaughtException();
- throw css::xml::sax::SAXException(
- "xmlsecurity: Exception in XSecParser::startElement",
- nullptr, exc);
- }
- catch (...)
- {
- throw css::xml::sax::SAXException(
- "xmlsecurity: unexpected exception in XSecParser::startElement", nullptr,
- css::uno::Any());
- }
-}
+};
-void SAL_CALL XSecParser::endElement( const OUString& aName )
+class XSecParser::LoSignatureLineIdContext
+ : public XSecParser::ReferencedContextImpl
{
- try
- {
- if (aName == "DigestValue" && !m_bInCertDigest)
+ private:
+ OUString m_Value;
+
+ public:
+ LoSignatureLineIdContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
{
- m_bInDigestValue = false;
}
- else if ( aName == "Reference" )
+
+ virtual void EndElement() override
{
- if ( m_bReferenceUnresolved )
- /*
- * it must be an octet stream
- */
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setSignatureLineId(m_Value);
+ }
+ else
{
- m_pXSecController->addStreamReference( m_currentReferenceURI, true, m_nReferenceDigestID );
- m_bReferenceUnresolved = false;
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineId");
}
+ }
- m_pXSecController->setDigestValue( m_nReferenceDigestID, m_ouDigestValue );
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
}
- else if ( aName == "SignedInfo" )
+};
+
+class XSecParser::LoSignatureLineValidImageContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoSignatureLineValidImageContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
{
- m_pXSecController->setReferenceCount();
}
- else if ( aName == "SignatureValue" )
+
+ virtual void EndElement() override
{
- m_pXSecController->setSignatureValue( m_ouSignatureValue );
- m_bInSignatureValue = false;
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setValidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineValidImage");
+ }
}
- else if (aName == "X509IssuerName")
+
+ virtual void Characters(OUString const& rChars) override
{
- m_pXSecController->setX509IssuerName( m_ouX509IssuerName );
- m_bInX509IssuerName = false;
+ m_Value += rChars;
}
- else if (aName == "X509SerialNumber")
+};
+
+class XSecParser::LoSignatureLineInvalidImageContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoSignatureLineInvalidImageContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
{
- m_pXSecController->setX509SerialNumber( m_ouX509SerialNumber );
- m_bInX509SerialNumber = false;
}
- else if (aName == "X509Certificate")
+
+ virtual void EndElement() override
{
- m_pXSecController->setX509Certificate( m_ouX509Certificate );
- m_bInX509Certificate = false;
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineInvalidImage");
+ }
}
- else if (aName == "PGPKeyID")
+
+ virtual void Characters(OUString const& rChars) override
{
- m_pXSecController->setGpgKeyID( m_ouGpgKeyID );
- m_bInGpgKeyID = false;
+ m_Value += rChars;
}
- else if (aName == "PGPKeyPacket")
+};
+
+class XSecParser::LoSignatureLineContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ LoSignatureLineContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
{
- m_pXSecController->setGpgCertificate( m_ouGpgCertificate );
- m_bInGpgCertificate = false;
}
- else if (aName == "loext:PGPOwner")
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
{
- m_pXSecController->setGpgOwner( m_ouGpgOwner );
- m_bInGpgOwner = false;
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineId")
+ {
+ return std::make_unique<LoSignatureLineIdContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineValidImage")
+ {
+ return std::make_unique<LoSignatureLineValidImageContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineInvalidImage")
+ {
+ return std::make_unique<LoSignatureLineInvalidImageContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "xd:CertDigest")
+};
+
+class XSecParser::XadesCertDigestContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rDigestValue;
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ XadesCertDigestContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rDigestValue, sal_Int32 & rReferenceDigestID)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rDigestValue(rDigestValue)
+ , m_rReferenceDigestID(rReferenceDigestID)
{
- m_pXSecController->setCertDigest( m_ouCertDigest );
- m_bInCertDigest = false;
}
- else if (aName == "xd:EncapsulatedX509Certificate" || aName == "xades:EncapsulatedX509Certificate")
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
{
- m_pXSecController->addEncapsulatedX509Certificate( m_ouEncapsulatedX509Certificate );
- m_bInEncapsulatedX509Certificate = false;
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_rReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rDigestValue);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "xd:SigningTime" || aName == "xades:SigningTime")
+};
+
+class XSecParser::XadesCertContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ OUString m_CertDigest;
+ OUString m_X509IssuerName;
+ OUString m_X509SerialNumber;
+
+ public:
+ XadesCertContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
{
- m_pXSecController->setDate( m_ouDate );
- m_bInSigningTime = false;
}
- else if (aName == "dc:date")
+
+ virtual void EndElement() override
{
- if (m_bInDate)
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setX509CertDigest(m_CertDigest, m_nReferenceDigestID, m_X509IssuerName, m_X509SerialNumber);
+ }
+ else
{
- m_pXSecController->setDate( m_ouDate );
- m_bInDate = false;
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned xades:Cert");
}
}
- else if (aName == "dc:description")
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
{
- m_pXSecController->setDescription( m_ouDescription );
- m_bInDescription = false;
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest")
+ {
+ return std::make_unique<XadesCertDigestContext>(m_rParser, std::move(pOldNamespaceMap), m_CertDigest, m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial")
+ {
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerName, m_X509SerialNumber);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "loext:SignatureLineId")
+};
+
+class XSecParser::XadesSigningCertificateContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSigningCertificateContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
{
- m_pXSecController->setSignatureLineId( m_ouSignatureLineId );
- m_bInSignatureLineId = false;
}
- else if (aName == "loext:SignatureLineValidImage")
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
{
- m_pXSecController->setValidSignatureImage( m_ouSignatureLineValidImage );
- m_bInSignatureLineValidImage = false;
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "Cert")
+ {
+ return std::make_unique<XadesCertContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
}
- else if (aName == "loext:SignatureLineInvalidImage")
+};
+
+class XSecParser::XadesSigningTimeContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ XadesSigningTimeContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
{
- m_pXSecController->setInvalidSignatureImage( m_ouSignatureLineInvalidImage );
- m_bInSignatureLineInvalidImage = false;
}
- if (m_xNextHandler.is())
+ virtual void EndElement() override
{
- m_xNextHandler->endElement(aName);
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setDate("", m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SigningTime");
+ }
}
- }
- catch (css::uno::Exception& )
- {//getCaughtException MUST be the first line in the catch block
- css::uno::Any exc = cppu::getCaughtException();
- throw css::xml::sax::SAXException(
- "xmlsecurity: Exception in XSecParser::endElement",
- nullptr, exc);
- }
- catch (...)
- {
- throw css::xml::sax::SAXException(
- "xmlsecurity: unexpected exception in XSecParser::endElement", nullptr,
- css::uno::Any());
- }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::XadesSignedSignaturePropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedSignaturePropertiesContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningTime")
+ {
+ return std::make_unique<XadesSigningTimeContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningCertificate")
+ {
+ return std::make_unique<XadesSigningCertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLine")
+ {
+ return std::make_unique<LoSignatureLineContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignaturePolicyIdentifier, xades:SignatureProductionPlace, xades:SignerRole
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesSignedPropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedPropertiesContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedSignatureProperties")
+ {
+ return std::make_unique<XadesSignedSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignedDataObjectProperties
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesQualifyingPropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesQualifyingPropertiesContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedProperties")
+ {
+ return std::make_unique<XadesSignedPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "UnsignedProperties")
+ {
+ return std::make_unique<XadesUnsignedPropertiesContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DcDateContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DcDateContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DcDescriptionContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DcDescriptionContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ OUString & rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsSignaturePropertyContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ enum class SignatureProperty { Unknown, Date, Description };
+ SignatureProperty m_Property = SignatureProperty::Unknown;
+ OUString m_Id;
+ OUString m_Value;
+
+ public:
+ DsSignaturePropertyContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_Id = CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ switch (m_Property)
+ {
+ case SignatureProperty::Unknown:
+ SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored");
+ break;
+ case SignatureProperty::Date:
+ m_rParser.m_pXSecController->setDate(m_Id, m_Value);
+ break;
+ case SignatureProperty::Description:
+ m_rParser.m_pXSecController->setDescription(m_Id, m_Value);
+ break;
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureProperty");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DC && rName == "date")
+ {
+ m_Property = SignatureProperty::Date;
+ return std::make_unique<DcDateContext>(m_rParser, std::move(pOldNamespaceMap), m_Value);
+ }
+ if (nNamespace == XML_NAMESPACE_DC && rName == "description")
+ {
+ m_Property = SignatureProperty::Description;
+ return std::make_unique<DcDescriptionContext>(m_rParser, std::move(pOldNamespaceMap), m_Value);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsSignaturePropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ DsSignaturePropertiesContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperty")
+ {
+ return std::make_unique<DsSignaturePropertyContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsObjectContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ DsObjectContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ // init with "false" here - the Signature element can't be referenced by its child
+ : XSecParser::ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), false)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperties")
+ {
+ return std::make_unique<DsSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "QualifyingProperties")
+ {
+ return std::make_unique<XadesQualifyingPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: ds:Manifest
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsSignatureContext
+ : public XSecParser::Context
+{
+ public:
+ DsSignatureContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs));
+ m_rParser.m_rXMLSignatureHelper.StartVerifySignatureElement();
+ m_rParser.m_pXSecController->addSignature();
+ if (!ouIdAttr.isEmpty())
+ {
+ m_rParser.m_pXSecController->setId( ouIdAttr );
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignedInfo")
+ {
+ return std::make_unique<DsSignedInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureValue")
+ {
+ return std::make_unique<DsSignatureValueContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "KeyInfo")
+ {
+ return std::make_unique<DsKeyInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Object")
+ {
+ return std::make_unique<DsObjectContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsigSignaturesContext
+ : public XSecParser::Context
+{
+ public:
+ DsigSignaturesContext(XSecParser & rParser,
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Signature")
+ {
+ return std::make_unique<DsSignatureContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+
+XSecParser::XSecParser(XMLSignatureHelper& rXMLSignatureHelper,
+ XSecController* pXSecController)
+ : m_pNamespaceMap(new SvXMLNamespaceMap)
+ , m_pXSecController(pXSecController)
+ , m_rXMLSignatureHelper(rXMLSignatureHelper)
+{
+ using namespace xmloff::token;
+ m_pNamespaceMap->Add( GetXMLToken(XML_XML), GetXMLToken(XML_N_XML), XML_NAMESPACE_XML );
+ m_pNamespaceMap->Add( "_dsig_ooo", GetXMLToken(XML_N_DSIG_OOO), XML_NAMESPACE_DSIG_OOO );
+ m_pNamespaceMap->Add( "_dsig", GetXMLToken(XML_N_DSIG), XML_NAMESPACE_DSIG );
+ m_pNamespaceMap->Add( "_ds", GetXMLToken(XML_N_DS), XML_NAMESPACE_DS );
+ m_pNamespaceMap->Add( "_xades132", GetXMLToken(XML_N_XADES132), XML_NAMESPACE_XADES132);
+ m_pNamespaceMap->Add( "_xades141", GetXMLToken(XML_N_XADES141), XML_NAMESPACE_XADES141);
+ m_pNamespaceMap->Add( "_dc", GetXMLToken(XML_N_DC), XML_NAMESPACE_DC );
+ m_pNamespaceMap->Add( "_office_libo",
+ GetXMLToken(XML_N_LO_EXT), XML_NAMESPACE_LO_EXT);
}
-void SAL_CALL XSecParser::characters( const OUString& aChars )
+OUString XSecParser::HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
{
- if (m_bInX509IssuerName)
- {
- m_ouX509IssuerName += aChars;
- }
- else if (m_bInX509SerialNumber)
- {
- m_ouX509SerialNumber += aChars;
- }
- else if (m_bInX509Certificate)
+ OUString ouIdAttr = getIdAttr(xAttrs);
+ if (!ouIdAttr.isEmpty())
{
- m_ouX509Certificate += aChars;
+ m_pXSecController->collectToVerify( ouIdAttr );
}
- else if (m_bInGpgCertificate)
- {
- m_ouGpgCertificate += aChars;
- }
- else if (m_bInGpgKeyID)
+ return ouIdAttr;
+}
+
+OUString XSecParser::getIdAttr(const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+{
+ OUString ouIdAttr = xAttribs->getValueByName("id");
+
+ if (ouIdAttr.isEmpty())
{
- m_ouGpgKeyID += aChars;
+ ouIdAttr = xAttribs->getValueByName("Id");
}
- else if (m_bInGpgOwner)
+
+ return ouIdAttr;
+}
+
+/*
+ * XDocumentHandler
+ */
+void SAL_CALL XSecParser::startDocument( )
+{
+ if (m_xNextHandler.is())
{
- m_ouGpgOwner += aChars;
+ m_xNextHandler->startDocument();
}
- else if (m_bInSignatureValue)
+}
+
+void SAL_CALL XSecParser::endDocument( )
+{
+ if (m_xNextHandler.is())
{
- m_ouSignatureValue += aChars;
+ m_xNextHandler->endDocument();
}
- else if (m_bInDigestValue && !m_bInCertDigest)
+}
+
+void SAL_CALL XSecParser::startElement(
+ const OUString& rName,
+ const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+{
+ assert(m_pNamespaceMap);
+ std::unique_ptr<SvXMLNamespaceMap> pRewindMap(
+ SvXMLImport::processNSAttributes(m_pNamespaceMap, nullptr, xAttribs));
+
+ OUString localName;
+ sal_uInt16 const nPrefix(m_pNamespaceMap->GetKeyByAttrName(rName, &localName));
+
+ std::unique_ptr<Context> pContext;
+
+ if (m_ContextStack.empty())
{
- m_ouDigestValue += aChars;
+ if ((nPrefix == XML_NAMESPACE_DSIG || nPrefix == XML_NAMESPACE_DSIG_OOO)
+ && localName == "document-signatures")
+ {
+ pContext.reset(new DsigSignaturesContext(*this, std::move(pRewindMap)));
+ }
+ else
+ {
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected root element", nullptr,
+ css::uno::Any());
+ }
}
- else if (m_bInDate)
+ else
{
- m_ouDate += aChars;
+ pContext = m_ContextStack.top()->CreateChildContext(
+ std::move(pRewindMap), nPrefix, localName);
}
- else if (m_bInDescription)
+
+ m_ContextStack.push(std::move(pContext));
+ assert(!pRewindMap);
+
+ try
{
- m_ouDescription += aChars;
+ m_ContextStack.top()->StartElement(xAttribs);
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->startElement(rName, xAttribs);
+ }
}
- else if (m_bInCertDigest)
- {
- m_ouCertDigest += aChars;
+ catch (css::uno::Exception& )
+ {//getCaughtException MUST be the first line in the catch block
+ css::uno::Any exc = cppu::getCaughtException();
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: Exception in XSecParser::startElement",
+ nullptr, exc);
}
- else if (m_bInEncapsulatedX509Certificate)
+ catch (...)
{
- m_ouEncapsulatedX509Certificate += aChars;
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected exception in XSecParser::startElement", nullptr,
+ css::uno::Any());
}
- else if (m_bInSigningTime)
+}
+
+void SAL_CALL XSecParser::endElement(const OUString& rName)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+
+ try
{
- m_ouDate += aChars;
+ m_ContextStack.top()->EndElement();
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->endElement(rName);
+ }
}
- else if (m_bInSignatureLineId)
- {
- m_ouSignatureLineId += aChars;
+ catch (css::uno::Exception& )
+ {//getCaughtException MUST be the first line in the catch block
+ css::uno::Any exc = cppu::getCaughtException();
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: Exception in XSecParser::endElement",
+ nullptr, exc);
}
- else if (m_bInSignatureLineValidImage)
+ catch (...)
{
- m_ouSignatureLineValidImage += aChars;
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected exception in XSecParser::endElement", nullptr,
+ css::uno::Any());
}
- else if (m_bInSignatureLineInvalidImage)
+
+ if (m_ContextStack.top()->m_pOldNamespaceMap)
{
- m_ouSignatureLineInvalidImage += aChars;
+ m_pNamespaceMap = std::move(m_ContextStack.top()->m_pOldNamespaceMap);
}
+ m_ContextStack.pop();
+}
+
+void SAL_CALL XSecParser::characters(const OUString& rChars)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+ m_ContextStack.top()->Characters(rChars);
if (m_xNextHandler.is())
{
- m_xNextHandler->characters(aChars);
+ m_xNextHandler->characters(rChars);
}
}
diff --git a/xmlsecurity/source/helper/xsecparser.hxx b/xmlsecurity/source/helper/xsecparser.hxx
index d9b079aa3116..7674bf28b84d 100644
--- a/xmlsecurity/source/helper/xsecparser.hxx
+++ b/xmlsecurity/source/helper/xsecparser.hxx
@@ -25,6 +25,10 @@
#include <cppuhelper/implbase.hxx>
+#include <xmloff/nmspmap.hxx>
+
+#include <stack>
+
class XMLSignatureHelper;
class XSecController;
@@ -48,47 +52,54 @@ class XSecParser: public cppu::WeakImplHelper
******************************************************************************/
{
friend class XSecController;
+public:
+ class Context;
private:
- /*
- * the following members are used to reserve the signature information,
- * including X509IssuerName, X509SerialNumber, and X509Certificate,etc.
- */
- OUString m_ouX509IssuerName;
- OUString m_ouX509SerialNumber;
- OUString m_ouX509Certificate;
- OUString m_ouGpgCertificate;
- OUString m_ouGpgKeyID;
- OUString m_ouGpgOwner;
- OUString m_ouCertDigest;
- OUString m_ouEncapsulatedX509Certificate;
- OUString m_ouDigestValue;
- OUString m_ouSignatureValue;
- OUString m_ouDate;
- /// Characters of a <dc:description> element, as just read from XML.
- OUString m_ouDescription;
- OUString m_ouSignatureLineId;
- OUString m_ouSignatureLineValidImage;
- OUString m_ouSignatureLineInvalidImage;
-
- /*
- * whether inside a particular element
- */
- bool m_bInX509IssuerName;
- bool m_bInX509SerialNumber;
- bool m_bInX509Certificate;
- bool m_bInGpgCertificate;
- bool m_bInGpgKeyID;
- bool m_bInGpgOwner;
- bool m_bInCertDigest;
- bool m_bInEncapsulatedX509Certificate;
- bool m_bInSigningTime;
- bool m_bInDigestValue;
- bool m_bInSignatureValue;
- bool m_bInDate;
- bool m_bInDescription;
- bool m_bInSignatureLineId;
- bool m_bInSignatureLineValidImage;
- bool m_bInSignatureLineInvalidImage;
+ class UnknownContext;
+ class ReferencedContextImpl;
+ class LoPGPOwnerContext;
+ class DsPGPKeyPacketContext;
+ class DsPGPKeyIDContext;
+ class DsPGPDataContext;
+ class DsX509CertificateContext;
+ class DsX509SerialNumberContext;
+ class DsX509IssuerNameContext;
+ class DsX509IssuerSerialContext;
+ class DsX509DataContext;
+ class DsKeyInfoContext;
+ class DsSignatureValueContext;
+ class DsDigestValueContext;
+ class DsDigestMethodContext;
+ class DsTransformContext;
+ class DsTransformsContext;
+ class DsReferenceContext;
+ class DsSignatureMethodContext;
+ class DsSignedInfoContext;
+ class XadesEncapsulatedX509CertificateContext;
+ class XadesCertificateValuesContext;
+ class XadesUnsignedSignaturePropertiesContext;
+ class XadesUnsignedPropertiesContext;
+ class LoSignatureLineIdContext;
+ class LoSignatureLineValidImageContext;
+ class LoSignatureLineInvalidImageContext;
+ class LoSignatureLineContext;
+ class XadesCertDigestContext;
+ class XadesCertContext;
+ class XadesSigningCertificateContext;
+ class XadesSigningTimeContext;
+ class XadesSignedSignaturePropertiesContext;
+ class XadesSignedPropertiesContext;
+ class XadesQualifyingPropertiesContext;
+ class DcDateContext;
+ class DcDescriptionContext;
+ class DsSignaturePropertyContext;
+ class DsSignaturePropertiesContext;
+ class DsObjectContext;
+ class DsSignatureContext;
+ class DsigSignaturesContext;
+
+ std::stack<std::unique_ptr<Context>> m_ContextStack;
+ std::unique_ptr<SvXMLNamespaceMap> m_pNamespaceMap;
/*
* the XSecController collaborating with XSecParser
@@ -101,22 +112,9 @@ private:
css::uno::Reference<
css::xml::sax::XDocumentHandler > m_xNextHandler;
- /*
- * this string is used to remember the current handled reference's URI,
- *
- * because it can be decided whether a stream reference is xml based or binary based
- * only after the Transforms element is read in, so we have to reserve the reference's
- * URI when the startElement event is met.
- */
- OUString m_currentReferenceURI;
- bool m_bReferenceUnresolved;
-
- // Relevant for ODF. The digest algorithm selected by the current DigestMethod element's
- // Algorithm attribute in the current Reference element. From css::xml::crypto::DigestID.
- sal_Int32 m_nReferenceDigestID;
XMLSignatureHelper& m_rXMLSignatureHelper;
-private:
+ OUString HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs);
static OUString getIdAttr(const css::uno::Reference<
css::xml::sax::XAttributeList >& xAttribs );
diff --git a/xmlsecurity/source/helper/xsecsign.cxx b/xmlsecurity/source/helper/xsecsign.cxx
index b9648ed64397..fd33a320d9bd 100644
--- a/xmlsecurity/source/helper/xsecsign.cxx
+++ b/xmlsecurity/source/helper/xsecsign.cxx
@@ -128,8 +128,8 @@ css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > XSecCon
if (nStorageFormat != embed::StorageFormats::OFOPXML)
{
internalSignatureInfor.signatureInfor.ouSignatureId = createId();
- internalSignatureInfor.signatureInfor.ouPropertyId = createId();
- internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, internalSignatureInfor.signatureInfor.ouPropertyId, -1, OUString() );
+ internalSignatureInfor.signatureInfor.ouDateTimePropertyId = createId();
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, internalSignatureInfor.signatureInfor.ouDateTimePropertyId, -1, OUString() );
size++;
if (bXAdESCompliantIfODF)
@@ -193,6 +193,7 @@ void XSecController::signAStream( sal_Int32 securityId, const OUString& uri, boo
}
}
+// note: this is called when creating a new signature from scratch
void XSecController::setX509Certificate(
sal_Int32 nSecurityId,
const OUString& ouX509IssuerName,
@@ -206,10 +207,13 @@ void XSecController::setX509Certificate(
if ( index == -1 )
{
InternalSignatureInformation isi(nSecurityId, nullptr);
- isi.signatureInfor.ouX509IssuerName = ouX509IssuerName;
- isi.signatureInfor.ouX509SerialNumber = ouX509SerialNumber;
- isi.signatureInfor.ouX509Certificate = ouX509Cert;
- isi.signatureInfor.ouCertDigest = ouX509CertDigest;
+ isi.signatureInfor.X509Datas.clear();
+ isi.signatureInfor.X509Datas.emplace_back();
+ isi.signatureInfor.X509Datas.back().emplace_back();
+ isi.signatureInfor.X509Datas.back().back().X509IssuerName = ouX509IssuerName;
+ isi.signatureInfor.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber;
+ isi.signatureInfor.X509Datas.back().back().X509Certificate = ouX509Cert;
+ isi.signatureInfor.X509Datas.back().back().CertDigest = ouX509CertDigest;
isi.signatureInfor.eAlgorithmID = eAlgorithmID;
m_vInternalSignatureInformations.push_back( isi );
}
@@ -217,16 +221,19 @@ void XSecController::setX509Certificate(
{
SignatureInformation &si
= m_vInternalSignatureInformations[index].signatureInfor;
- si.ouX509IssuerName = ouX509IssuerName;
- si.ouX509SerialNumber = ouX509SerialNumber;
- si.ouX509Certificate = ouX509Cert;
- si.ouCertDigest = ouX509CertDigest;
+ si.X509Datas.clear();
+ si.X509Datas.emplace_back();
+ si.X509Datas.back().emplace_back();
+ si.X509Datas.back().back().X509IssuerName = ouX509IssuerName;
+ si.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber;
+ si.X509Datas.back().back().X509Certificate = ouX509Cert;
+ si.X509Datas.back().back().CertDigest = ouX509CertDigest;
}
}
void XSecController::setGpgCertificate(
sal_Int32 nSecurityId,
- const OUString& ouCertDigest,
+ const OUString& ouKeyDigest,
const OUString& ouCert,
const OUString& ouOwner)
{
@@ -237,16 +244,17 @@ void XSecController::setGpgCertificate(
InternalSignatureInformation isi(nSecurityId, nullptr);
isi.signatureInfor.ouGpgCertificate = ouCert;
isi.signatureInfor.ouGpgOwner = ouOwner;
- isi.signatureInfor.ouCertDigest = ouCertDigest;
+ isi.signatureInfor.ouGpgKeyID = ouKeyDigest;
m_vInternalSignatureInformations.push_back( isi );
}
else
{
SignatureInformation &si
= m_vInternalSignatureInformations[index].signatureInfor;
+ si.X509Datas.clear(); // it is a PGP signature now
si.ouGpgCertificate = ouCert;
si.ouGpgOwner = ouOwner;
- si.ouCertDigest = ouCertDigest;
+ si.ouGpgKeyID = ouKeyDigest;
}
}
diff --git a/xmlsecurity/source/helper/xsecverify.cxx b/xmlsecurity/source/helper/xsecverify.cxx
index c826971b1c7d..89141ed1dfd4 100644
--- a/xmlsecurity/source/helper/xsecverify.cxx
+++ b/xmlsecurity/source/helper/xsecverify.cxx
@@ -22,6 +22,7 @@
#include <xsecctl.hxx>
#include "xsecparser.hxx"
#include "ooxmlsecparser.hxx"
+#include <biginteger.hxx>
#include <framework/signatureverifierimpl.hxx>
#include <framework/saxeventkeeperimpl.hxx>
#include <gpg/xmlsignature_gpgimpl.hxx>
@@ -143,6 +144,25 @@ void XSecController::switchGpgSignature()
#endif
}
+bool XSecController::haveReferenceForId(OUString const& rId) const
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::haveReferenceForId: no signature");
+ return false;
+ }
+ InternalSignatureInformation const& rIsi(m_vInternalSignatureInformations.back());
+ for (SignatureReferenceInformation const& rSri : rIsi.signatureInfor.vSignatureReferenceInfors)
+ {
+ if (rSri.nType == SignatureReferenceType::SAMEDOCUMENT
+ && rSri.ouURI == rId) // ouUri has # stripped
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
void XSecController::addReference( const OUString& ouUri, sal_Int32 nDigestID, const OUString& ouType )
{
if (m_vInternalSignatureInformations.empty())
@@ -221,7 +241,9 @@ void XSecController::setReferenceCount() const
}
}
-void XSecController::setX509IssuerName( OUString const & ouX509IssuerName )
+void XSecController::setX509Data(
+ std::vector<std::pair<OUString, OUString>> & rX509IssuerSerials,
+ std::vector<OUString> const& rX509Certificates)
{
if (m_vInternalSignatureInformations.empty())
{
@@ -229,29 +251,52 @@ void XSecController::setX509IssuerName( OUString const & ouX509IssuerName )
return;
}
InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
- isi.signatureInfor.ouX509IssuerName = ouX509IssuerName;
-}
-
-void XSecController::setX509SerialNumber( OUString const & ouX509SerialNumber )
-{
- if (m_vInternalSignatureInformations.empty())
+ SignatureInformation::X509Data data;
+ // due to the excessive flexibility of the spec it's possible that there
+ // is both a reference to a cert and the cert itself in one X509Data
+ for (OUString const& it : rX509Certificates)
{
- SAL_INFO("xmlsecurity.helper","XSecController::setX509SerialNumber: no signature");
- return;
+ try
+ {
+ data.emplace_back();
+ data.back().X509Certificate = it;
+ uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(m_xSecurityContext->getSecurityEnvironment());
+ uno::Reference<security::XCertificate> const xCert(xSecEnv->createCertificateFromAscii(it));
+ if (!xCert.is())
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ continue; // will be handled in CheckX509Data
+ }
+ OUString const issuerName(xCert->getIssuerName());
+ OUString const serialNumber(xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()));
+ auto const iter = std::find_if(rX509IssuerSerials.begin(), rX509IssuerSerials.end(),
+ [&](auto const& rX509IssuerSerial) {
+ return xmlsecurity::EqualDistinguishedNames(issuerName, rX509IssuerSerial.first)
+ && serialNumber == rX509IssuerSerial.second;
+ });
+ if (iter != rX509IssuerSerials.end())
+ {
+ data.back().X509IssuerName = iter->first;
+ data.back().X509SerialNumber = iter->second;
+ rX509IssuerSerials.erase(iter);
+ }
+ }
+ catch (uno::Exception const&)
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ }
}
- InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
- isi.signatureInfor.ouX509SerialNumber = ouX509SerialNumber;
-}
-
-void XSecController::setX509Certificate( OUString const & ouX509Certificate )
-{
- if (m_vInternalSignatureInformations.empty())
+ // now handle any that are left...
+ for (auto const& it : rX509IssuerSerials)
{
- SAL_INFO("xmlsecurity.helper","XSecController::setX509Certificate: no signature");
- return;
+ data.emplace_back();
+ data.back().X509IssuerName = it.first;
+ data.back().X509SerialNumber = it.second;
+ }
+ if (!data.empty())
+ {
+ isi.signatureInfor.X509Datas.push_back(data);
}
- InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
- isi.signatureInfor.ouX509Certificate = ouX509Certificate;
}
void XSecController::setSignatureValue( OUString const & ouSignatureValue )
@@ -317,7 +362,7 @@ void XSecController::setGpgOwner( OUString const & ouGpgOwner )
isi.signatureInfor.ouGpgOwner = ouGpgOwner;
}
-void XSecController::setDate( OUString const & ouDate )
+void XSecController::setDate(OUString const& rId, OUString const& ouDate)
{
if (m_vInternalSignatureInformations.empty())
{
@@ -325,17 +370,31 @@ void XSecController::setDate( OUString const & ouDate )
return;
}
InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ // there may be multiple timestamps in a signature - check them for consistency
+ if (!isi.signatureInfor.ouDateTime.isEmpty()
+ && isi.signatureInfor.ouDateTime != ouDate)
+ {
+ isi.signatureInfor.hasInconsistentSigningTime = true;
+ }
(void)utl::ISO8601parseDateTime( ouDate, isi.signatureInfor.stDateTime);
isi.signatureInfor.ouDateTime = ouDate;
+ if (!rId.isEmpty())
+ {
+ isi.signatureInfor.ouDateTimePropertyId = rId;
+ }
}
-void XSecController::setDescription(const OUString& rDescription)
+void XSecController::setDescription(OUString const& rId, OUString const& rDescription)
{
if (m_vInternalSignatureInformations.empty())
return;
InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
rInformation.signatureInfor.ouDescription = rDescription;
+ if (!rId.isEmpty())
+ {
+ rInformation.signatureInfor.ouDescriptionPropertyId = rId;
+ }
}
void XSecController::setSignatureBytes(const uno::Sequence<sal_Int8>& rBytes)
@@ -347,13 +406,67 @@ void XSecController::setSignatureBytes(const uno::Sequence<sal_Int8>& rBytes)
rInformation.signatureInfor.aSignatureBytes = rBytes;
}
-void XSecController::setCertDigest(const OUString& rCertDigest)
+void XSecController::setX509CertDigest(
+ OUString const& rCertDigest, sal_Int32 const /*TODO nReferenceDigestID*/,
+ OUString const& rX509IssuerName, OUString const& rX509SerialNumber)
{
if (m_vInternalSignatureInformations.empty())
return;
InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
- rInformation.signatureInfor.ouCertDigest = rCertDigest;
+ for (auto & rData : rInformation.signatureInfor.X509Datas)
+ {
+ for (auto & it : rData)
+ {
+ if (xmlsecurity::EqualDistinguishedNames(it.X509IssuerName, rX509IssuerName)
+ && it.X509SerialNumber == rX509SerialNumber)
+ {
+ it.CertDigest = rCertDigest;
+ return;
+ }
+ }
+ }
+ // fall-back: read the actual certificates
+ for (auto & rData : rInformation.signatureInfor.X509Datas)
+ {
+ for (auto & it : rData)
+ {
+ if (!it.X509Certificate.isEmpty())
+ {
+ try
+ {
+ uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(m_xSecurityContext->getSecurityEnvironment());
+ uno::Reference<security::XCertificate> const xCert(xSecEnv->createCertificateFromAscii(it.X509Certificate));
+ if (!xCert.is())
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ }
+ else if (xmlsecurity::EqualDistinguishedNames(xCert->getIssuerName(),rX509IssuerName)
+ && xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()) == rX509SerialNumber)
+ {
+ it.CertDigest = rCertDigest;
+ // note: testInsertCertificate_PEM_DOCX requires these!
+ it.X509SerialNumber = rX509SerialNumber;
+ it.X509IssuerName = rX509IssuerName;
+ return;
+ }
+ }
+ catch (uno::Exception const&)
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ }
+ }
+ }
+ }
+ if (!rInformation.signatureInfor.ouGpgCertificate.isEmpty())
+ {
+ SAL_INFO_IF(rCertDigest != rInformation.signatureInfor.ouGpgKeyID,
+ "xmlsecurity.helper", "PGPKeyID vs CertDigest mismatch");
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot find X509Data for CertDigest");
+ }
}
namespace {
@@ -429,27 +542,6 @@ void XSecController::setId( OUString const & ouId )
isi.signatureInfor.ouSignatureId = ouId;
}
-void XSecController::setPropertyId( OUString const & ouPropertyId )
-{
- if (m_vInternalSignatureInformations.empty())
- {
- SAL_INFO("xmlsecurity.helper","XSecController::setPropertyId: no signature");
- return;
- }
- InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
-
- if (isi.signatureInfor.ouPropertyId.isEmpty())
- {
- // <SignatureProperty> ID attribute is for the date.
- isi.signatureInfor.ouPropertyId = ouPropertyId;
- }
- else
- {
- // <SignatureProperty> ID attribute is for the description.
- isi.signatureInfor.ouDescriptionPropertyId = ouPropertyId;
- }
-}
-
/* public: for signature verify */
void XSecController::collectToVerify( const OUString& referenceId )
{
diff --git a/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx
index 01b27fb9756f..0ac0d6216c96 100644
--- a/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx
+++ b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx
@@ -753,7 +753,7 @@ sal_Int32 SecurityEnvironment_MSCryptImpl::verifyCertificate(
const uno::Reference< css::security::XCertificate >& aCert,
const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts)
{
- sal_Int32 validity = 0;
+ sal_Int32 validity = css::security::CertificateValidity::INVALID;
PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
PCCERT_CONTEXT pCertContext = nullptr;
@@ -897,7 +897,7 @@ sal_Int32 SecurityEnvironment_MSCryptImpl::verifyCertificate(
}
else
{
- SAL_INFO("xmlsecurity.xmlsec", "CertGetCertificateChaine failed.");
+ SAL_INFO("xmlsecurity.xmlsec", "CertGetCertificateChain failed.");
}
}
diff --git a/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx
index 0e619b2802f8..244cd46ac564 100644
--- a/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx
+++ b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx
@@ -26,6 +26,7 @@
#include <cppuhelper/supportsservice.hxx>
#include "x509certificate_mscryptimpl.hxx"
#include <certificateextension_xmlsecimpl.hxx>
+#include <biginteger.hxx>
#include "sanextension_mscryptimpl.hxx"
#include "oid.hxx"
@@ -649,4 +650,50 @@ Sequence<OUString> SAL_CALL X509Certificate_MSCryptImpl::getSupportedServiceName
return { OUString() };
}
+namespace xmlsecurity {
+
+static bool EncodeDistinguishedName(OUString const& rName, CERT_NAME_BLOB & rBlob)
+{
+ LPCWSTR pszError;
+ if (!CertStrToNameW(X509_ASN_ENCODING,
+ reinterpret_cast<LPCWSTR>(rName.getStr()), CERT_X500_NAME_STR,
+ nullptr, nullptr, &rBlob.cbData, &pszError))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << reinterpret_cast<char16_t const*>(pszError));
+ return false;
+ }
+ rBlob.pbData = new BYTE[rBlob.cbData];
+ if (!CertStrToNameW(X509_ASN_ENCODING,
+ reinterpret_cast<LPCWSTR>(rName.getStr()), CERT_X500_NAME_STR,
+ nullptr, rBlob.pbData, &rBlob.cbData, &pszError))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << reinterpret_cast<char16_t const*>(pszError));
+ return false;
+ }
+ return true;
+}
+
+bool EqualDistinguishedNames(
+ OUString const& rName1, OUString const& rName2)
+{
+ CERT_NAME_BLOB blob1;
+ if (!EncodeDistinguishedName(rName1, blob1))
+ {
+ return false;
+ }
+ CERT_NAME_BLOB blob2;
+ if (!EncodeDistinguishedName(rName2, blob2))
+ {
+ delete[] blob1.pbData;
+ return false;
+ }
+ bool const ret(CertCompareCertificateName(X509_ASN_ENCODING,
+ &blob1, &blob2) == TRUE);
+ delete[] blob2.pbData;
+ delete[] blob1.pbData;
+ return ret;
+}
+
+} // namespace xmlsecurity
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx
index dcbad0348091..359e4ce2dd49 100644
--- a/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx
+++ b/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx
@@ -18,6 +18,7 @@
*/
#include <sal/config.h>
+#include <sal/log.hxx>
#include <rtl/uuid.h>
#include <xmlsec-wrapper.h>
@@ -232,6 +233,10 @@ SAL_CALL XMLSignature_MSCryptImpl::validate(
// We do certificate verification ourselves.
pDsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS;
+ // limit possible key data to valid X509 certificates only, no KeyValues
+ if (xmlSecPtrListAdd(&(pDsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecMSCngKeyDataX509GetKlass()) < 0)
+ throw RuntimeException("failed to limit allowed key data");
+
//Verify signature
//The documentation says that the signature is only valid if the return value is 0 (that is, not < 0)
//AND pDsigCtx->status == xmlSecDSigStatusSucceeded. That is, we must not make any assumptions, if
@@ -254,6 +259,7 @@ SAL_CALL XMLSignature_MSCryptImpl::validate(
++nReferenceGood;
}
}
+ SAL_INFO("xmlsecurity.xmlsec", "xmlSecDSigCtxVerify status " << pDsigCtx->status << ", references good " << nReferenceGood << " of " << nReferenceCount);
if (rs == 0 && nReferenceCount == nReferenceGood)
{
diff --git a/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx
index 1a323d33f32f..d6143a81883c 100644
--- a/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx
+++ b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx
@@ -31,6 +31,7 @@
#include <rtl/ref.hxx>
#include "x509certificate_nssimpl.hxx"
+#include <biginteger.hxx>
#include <certificateextension_xmlsecimpl.hxx>
#include "sanextension_nssimpl.hxx"
@@ -533,4 +534,28 @@ sal_Bool SAL_CALL X509Certificate_NssImpl::supportsService(const OUString& servi
/* XServiceInfo */
Sequence<OUString> SAL_CALL X509Certificate_NssImpl::getSupportedServiceNames() { return { OUString() }; }
+namespace xmlsecurity {
+
+bool EqualDistinguishedNames(
+ OUString const& rName1, OUString const& rName2)
+{
+ CERTName *const pName1(CERT_AsciiToName(OUStringToOString(rName1, RTL_TEXTENCODING_UTF8).getStr()));
+ if (pName1 == nullptr)
+ {
+ return false;
+ }
+ CERTName *const pName2(CERT_AsciiToName(OUStringToOString(rName2, RTL_TEXTENCODING_UTF8).getStr()));
+ if (pName2 == nullptr)
+ {
+ CERT_DestroyName(pName1);
+ return false;
+ }
+ bool const ret(CERT_CompareName(pName1, pName2) == SECEqual);
+ CERT_DestroyName(pName2);
+ CERT_DestroyName(pName1);
+ return ret;
+}
+
+} // namespace xmlsecurity
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx
index 32e4335a5207..fde4b747e932 100644
--- a/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx
+++ b/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx
@@ -26,6 +26,8 @@
#include "securityenvironment_nssimpl.hxx"
+#include <sal/log.hxx>
+
#include <com/sun/star/xml/crypto/XXMLSignature.hpp>
#include <memory>
@@ -245,6 +247,10 @@ SAL_CALL XMLSignature_NssImpl::validate(
// We do certificate verification ourselves.
pDsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS;
+ // limit possible key data to valid X509 certificates only, no KeyValues
+ if (xmlSecPtrListAdd(&(pDsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecNssKeyDataX509GetKlass()) < 0)
+ throw RuntimeException("failed to limit allowed key data");
+
//Verify signature
int rs = xmlSecDSigCtxVerify( pDsigCtx.get() , pNode );
@@ -261,6 +267,7 @@ SAL_CALL XMLSignature_NssImpl::validate(
++nReferenceGood;
}
}
+ SAL_INFO("xmlsecurity.xmlsec", "xmlSecDSigCtxVerify status " << pDsigCtx->status << ", references good " << nReferenceGood << " of " << nReferenceCount);
if (rs == 0 && pDsigCtx->status == xmlSecDSigStatusSucceeded && nReferenceCount == nReferenceGood)
{