summaryrefslogtreecommitdiffstats
path: root/sfx2/source/view/classificationhelper.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/view/classificationhelper.cxx')
-rw-r--r--sfx2/source/view/classificationhelper.cxx985
1 files changed, 985 insertions, 0 deletions
diff --git a/sfx2/source/view/classificationhelper.cxx b/sfx2/source/view/classificationhelper.cxx
new file mode 100644
index 000000000..4494e7e7c
--- /dev/null
+++ b/sfx2/source/view/classificationhelper.cxx
@@ -0,0 +1,985 @@
+/* -*- 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 <sfx2/classificationhelper.hxx>
+
+#include <map>
+#include <algorithm>
+#include <iterator>
+
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <sfx2/infobar.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <tools/datetime.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/datetime.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/weld.hxx>
+#include <svl/fstathelper.hxx>
+
+#include <o3tl/string_view.hxx>
+#include <officecfg/Office/Common.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+
+const OUString& PROP_BACNAME()
+{
+ static const OUString sProp("BusinessAuthorizationCategory:Name");
+ return sProp;
+}
+
+const OUString& PROP_STARTVALIDITY()
+{
+ static const OUString sProp("Authorization:StartValidity");
+ return sProp;
+}
+
+const OUString& PROP_NONE()
+{
+ static const OUString sProp("None");
+ return sProp;
+}
+
+const OUString& PROP_IMPACTSCALE()
+{
+ static const OUString sProp("Impact:Scale");
+ return sProp;
+}
+
+const OUString& PROP_IMPACTLEVEL()
+{
+ static const OUString sProp("Impact:Level:Confidentiality");
+ return sProp;
+}
+
+const OUString& PROP_PREFIX_EXPORTCONTROL()
+{
+ static const OUString sProp("urn:bails:ExportControl:");
+ return sProp;
+}
+
+const OUString& PROP_PREFIX_NATIONALSECURITY()
+{
+ static const OUString sProp("urn:bails:NationalSecurity:");
+ return sProp;
+}
+
+/// Represents one category of a classification policy.
+class SfxClassificationCategory
+{
+public:
+ /// PROP_BACNAME() is stored separately for easier lookup.
+ OUString m_aName;
+ OUString m_aAbbreviatedName; //< An abbreviation to display instead of m_aName.
+ OUString m_aIdentifier; //< The Identifier of this entry.
+ size_t m_nConfidentiality; //< 0 is the lowest (least-sensitive).
+ std::map<OUString, OUString> m_aLabels;
+};
+
+/// Parses a policy XML conforming to the TSCP BAF schema.
+class SfxClassificationParser : public cppu::WeakImplHelper<xml::sax::XDocumentHandler>
+{
+public:
+ std::vector<SfxClassificationCategory> m_aCategories;
+ std::vector<OUString> m_aMarkings;
+ std::vector<OUString> m_aIPParts;
+ std::vector<OUString> m_aIPPartNumbers;
+
+ OUString m_aPolicyAuthorityName;
+ bool m_bInPolicyAuthorityName = false;
+ OUString m_aPolicyName;
+ bool m_bInPolicyName = false;
+ OUString m_aProgramID;
+ bool m_bInProgramID = false;
+ OUString m_aScale;
+ bool m_bInScale = false;
+ OUString m_aConfidentalityValue;
+ bool m_bInConfidentalityValue = false;
+ OUString m_aIdentifier;
+ bool m_bInIdentifier = false;
+ OUString m_aValue;
+ bool m_bInValue = false;
+
+ /// Pointer to a value in m_aCategories, the currently parsed category.
+ SfxClassificationCategory* m_pCategory = nullptr;
+
+ SfxClassificationParser();
+
+ void SAL_CALL startDocument() override;
+
+ void SAL_CALL endDocument() override;
+
+ void SAL_CALL startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs) override;
+
+ void SAL_CALL endElement(const OUString& rName) override;
+
+ void SAL_CALL characters(const OUString& rChars) override;
+
+ void SAL_CALL ignorableWhitespace(const OUString& rWhitespaces) override;
+
+ void SAL_CALL processingInstruction(const OUString& rTarget, const OUString& rData) override;
+
+ void SAL_CALL setDocumentLocator(const uno::Reference<xml::sax::XLocator>& xLocator) override;
+};
+
+SfxClassificationParser::SfxClassificationParser() = default;
+
+void SAL_CALL SfxClassificationParser::startDocument()
+{
+}
+
+void SAL_CALL SfxClassificationParser::endDocument()
+{
+}
+
+void SAL_CALL SfxClassificationParser::startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs)
+{
+ if (rName == "baf:PolicyAuthorityName")
+ {
+ m_aPolicyAuthorityName.clear();
+ m_bInPolicyAuthorityName = true;
+ }
+ else if (rName == "baf:PolicyName")
+ {
+ m_aPolicyName.clear();
+ m_bInPolicyName = true;
+ }
+ else if (rName == "baf:ProgramID")
+ {
+ m_aProgramID.clear();
+ m_bInProgramID = true;
+ }
+ else if (rName == "baf:BusinessAuthorizationCategory")
+ {
+ const OUString aName = xAttribs->getValueByName("Name");
+ if (!m_pCategory && !aName.isEmpty())
+ {
+ OUString aIdentifier = xAttribs->getValueByName("Identifier");
+
+ // Create a new category and initialize it with the data that's true for all categories.
+ m_aCategories.emplace_back();
+ SfxClassificationCategory& rCategory = m_aCategories.back();
+
+ rCategory.m_aName = aName;
+ // Set the abbreviated name, if any, otherwise fallback on the full name.
+ const OUString aAbbreviatedName = xAttribs->getValueByName("loextAbbreviatedName");
+ rCategory.m_aAbbreviatedName = !aAbbreviatedName.isEmpty() ? aAbbreviatedName : aName;
+ rCategory.m_aIdentifier = aIdentifier;
+
+ rCategory.m_aLabels["PolicyAuthority:Name"] = m_aPolicyAuthorityName;
+ rCategory.m_aLabels["Policy:Name"] = m_aPolicyName;
+ rCategory.m_aLabels["BusinessAuthorization:Identifier"] = m_aProgramID;
+ rCategory.m_aLabels["BusinessAuthorizationCategory:Identifier"] = aIdentifier;
+
+ // Also initialize defaults.
+ rCategory.m_aLabels["PolicyAuthority:Identifier"] = PROP_NONE();
+ rCategory.m_aLabels["PolicyAuthority:Country"] = PROP_NONE();
+ rCategory.m_aLabels["Policy:Identifier"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorization:Name"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorization:Locator"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorizationCategory:Identifier:OID"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorizationCategory:Locator"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorization:Locator"] = PROP_NONE();
+ rCategory.m_aLabels["MarkingPrecedence"] = PROP_NONE();
+ rCategory.m_aLabels["Marking:general-summary"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement:ext:2"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement:ext:3"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement:ext:4"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement:ext:2"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement:ext:3"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement:ext:4"].clear();
+ rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCHEADER()].clear();
+ rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCFOOTER()].clear();
+ rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCWATERMARK()].clear();
+ rCategory.m_aLabels["Marking:email-first-line-of-text"].clear();
+ rCategory.m_aLabels["Marking:email-last-line-of-text"].clear();
+ rCategory.m_aLabels["Marking:email-subject-prefix"].clear();
+ rCategory.m_aLabels["Marking:email-subject-suffix"].clear();
+ rCategory.m_aLabels[PROP_STARTVALIDITY()] = PROP_NONE();
+ rCategory.m_aLabels["Authorization:StopValidity"] = PROP_NONE();
+ m_pCategory = &rCategory;
+ }
+ }
+ else if (rName == "loext:Marking")
+ {
+ OUString aName = xAttribs->getValueByName("Name");
+ m_aMarkings.push_back(aName);
+ }
+ else if (rName == "loext:IntellectualPropertyPart")
+ {
+ OUString aName = xAttribs->getValueByName("Name");
+ m_aIPParts.push_back(aName);
+ }
+ else if (rName == "loext:IntellectualPropertyPartNumber")
+ {
+ OUString aName = xAttribs->getValueByName("Name");
+ m_aIPPartNumbers.push_back(aName);
+ }
+ else if (rName == "baf:Scale")
+ {
+ m_aScale.clear();
+ m_bInScale = true;
+ }
+ else if (rName == "baf:ConfidentalityValue")
+ {
+ m_aConfidentalityValue.clear();
+ m_bInConfidentalityValue = true;
+ }
+ else if (rName == "baf:Identifier")
+ {
+ m_aIdentifier.clear();
+ m_bInIdentifier = true;
+ }
+ else if (rName == "baf:Value")
+ {
+ m_aValue.clear();
+ m_bInValue = true;
+ }
+}
+
+void SAL_CALL SfxClassificationParser::endElement(const OUString& rName)
+{
+ if (rName == "baf:PolicyAuthorityName")
+ m_bInPolicyAuthorityName = false;
+ else if (rName == "baf:PolicyName")
+ m_bInPolicyName = false;
+ else if (rName == "baf:ProgramID")
+ m_bInProgramID = false;
+ else if (rName == "baf:BusinessAuthorizationCategory")
+ m_pCategory = nullptr;
+ else if (rName == "baf:Scale")
+ {
+ m_bInScale = false;
+ if (m_pCategory)
+ m_pCategory->m_aLabels[PROP_IMPACTSCALE()] = m_aScale;
+ }
+ else if (rName == "baf:ConfidentalityValue")
+ {
+ m_bInConfidentalityValue = false;
+ if (m_pCategory)
+ {
+ std::map<OUString, OUString>& rLabels = m_pCategory->m_aLabels;
+ rLabels[PROP_IMPACTLEVEL()] = m_aConfidentalityValue;
+ m_pCategory->m_nConfidentiality = m_aConfidentalityValue.toInt32(); // 0-based class sensitivity; 0 is lowest.
+ // Set the two other type of levels as well, if they're not set
+ // yet: they're optional in BAF, but not in BAILS.
+ rLabels.try_emplace("Impact:Level:Integrity", m_aConfidentalityValue);
+ rLabels.try_emplace("Impact:Level:Availability", m_aConfidentalityValue);
+ }
+ }
+ else if (rName == "baf:Identifier")
+ m_bInIdentifier = false;
+ else if (rName == "baf:Value")
+ {
+ if (m_pCategory)
+ {
+ if (m_aIdentifier == "Document: Header")
+ m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCHEADER()] = m_aValue;
+ else if (m_aIdentifier == "Document: Footer")
+ m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCFOOTER()] = m_aValue;
+ else if (m_aIdentifier == "Document: Watermark")
+ m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCWATERMARK()] = m_aValue;
+ }
+ }
+}
+
+void SAL_CALL SfxClassificationParser::characters(const OUString& rChars)
+{
+ if (m_bInPolicyAuthorityName)
+ m_aPolicyAuthorityName += rChars;
+ else if (m_bInPolicyName)
+ m_aPolicyName += rChars;
+ else if (m_bInProgramID)
+ m_aProgramID += rChars;
+ else if (m_bInScale)
+ m_aScale += rChars;
+ else if (m_bInConfidentalityValue)
+ m_aConfidentalityValue += rChars;
+ else if (m_bInIdentifier)
+ m_aIdentifier += rChars;
+ else if (m_bInValue)
+ m_aValue += rChars;
+}
+
+void SAL_CALL SfxClassificationParser::ignorableWhitespace(const OUString& /*rWhitespace*/)
+{
+}
+
+void SAL_CALL SfxClassificationParser::processingInstruction(const OUString& /*rTarget*/, const OUString& /*rData*/)
+{
+}
+
+void SAL_CALL SfxClassificationParser::setDocumentLocator(const uno::Reference<xml::sax::XLocator>& /*xLocator*/)
+{
+}
+
+} // anonymous namespace
+
+/// Implementation details of SfxClassificationHelper.
+class SfxClassificationHelper::Impl
+{
+public:
+ /// Selected categories, one category for each policy type.
+ std::map<SfxClassificationPolicyType, SfxClassificationCategory> m_aCategory;
+ /// Possible categories of a policy to choose from.
+ std::vector<SfxClassificationCategory> m_aCategories;
+ std::vector<OUString> m_aMarkings;
+ std::vector<OUString> m_aIPParts;
+ std::vector<OUString> m_aIPPartNumbers;
+
+ uno::Reference<document::XDocumentProperties> m_xDocumentProperties;
+
+ bool m_bUseLocalized;
+
+ explicit Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized);
+ void parsePolicy();
+ /// Synchronize m_aLabels back to the document properties.
+ void pushToDocumentProperties();
+ /// Set the classification start date to the system time.
+ void setStartValidity(SfxClassificationPolicyType eType);
+};
+
+SfxClassificationHelper::Impl::Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized)
+ : m_xDocumentProperties(std::move(xDocumentProperties))
+ , m_bUseLocalized(bUseLocalized)
+{
+ parsePolicy();
+}
+
+void SfxClassificationHelper::Impl::parsePolicy()
+{
+ uno::Reference<uno::XComponentContext> xComponentContext = comphelper::getProcessComponentContext();
+ SvtPathOptions aOptions;
+ OUString aPath = aOptions.GetClassificationPath();
+
+ // See if there is a localized variant next to the configured XML.
+ OUString aExtension(".xml");
+ if (aPath.endsWith(aExtension) && m_bUseLocalized)
+ {
+ std::u16string_view aBase = aPath.subView(0, aPath.getLength() - aExtension.getLength());
+ const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
+ // Expected format is "<original path>_xx-XX.xml".
+ OUString aLocalized = OUString::Concat(aBase) + "_" + rLanguageTag.getBcp47() + aExtension;
+ if (FStatHelper::IsDocument(aLocalized))
+ aPath = aLocalized;
+ }
+
+ std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(aPath, StreamMode::READ);
+ uno::Reference<io::XInputStream> xInputStream(new utl::OStreamWrapper(std::move(pStream)));
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xComponentContext);
+ rtl::Reference<SfxClassificationParser> xClassificationParser(new SfxClassificationParser());
+ xParser->setDocumentHandler(xClassificationParser);
+ try
+ {
+ xParser->parseStream(aParserInput);
+ }
+ catch (const xml::sax::SAXParseException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.view", "parsePolicy() failed");
+ }
+ m_aCategories = xClassificationParser->m_aCategories;
+ m_aMarkings = xClassificationParser->m_aMarkings;
+ m_aIPParts = xClassificationParser->m_aIPParts;
+ m_aIPPartNumbers = xClassificationParser->m_aIPPartNumbers;
+}
+
+static bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties, std::u16string_view rName)
+{
+ return std::any_of(rProperties.begin(), rProperties.end(), [&](const beans::Property& rProperty)
+ {
+ return rProperty.Name == rName;
+ });
+}
+
+void SfxClassificationHelper::Impl::setStartValidity(SfxClassificationPolicyType eType)
+{
+ auto itCategory = m_aCategory.find(eType);
+ if (itCategory == m_aCategory.end())
+ return;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(policyTypeToString(eType) + PROP_STARTVALIDITY());
+ if (it != rCategory.m_aLabels.end())
+ {
+ if (it->second == PROP_NONE())
+ {
+ // The policy left the start date unchanged, replace it with the system time.
+ util::DateTime aDateTime = DateTime(DateTime::SYSTEM).GetUNODateTime();
+ it->second = utl::toISO8601(aDateTime);
+ }
+ }
+}
+
+void SfxClassificationHelper::Impl::pushToDocumentProperties()
+{
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = m_xDocumentProperties->getUserDefinedProperties();
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
+ for (auto& rPair : m_aCategory)
+ {
+ SfxClassificationPolicyType eType = rPair.first;
+ SfxClassificationCategory& rCategory = rPair.second;
+ std::map<OUString, OUString> aLabels = rCategory.m_aLabels;
+ aLabels[policyTypeToString(eType) + PROP_BACNAME()] = rCategory.m_aName;
+ for (const auto& rLabel : aLabels)
+ {
+ try
+ {
+ if (lcl_containsProperty(aProperties, rLabel.first))
+ xPropertySet->setPropertyValue(rLabel.first, uno::Any(rLabel.second));
+ else
+ xPropertyContainer->addProperty(rLabel.first, beans::PropertyAttribute::REMOVABLE, uno::Any(rLabel.second));
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.view", "pushDocumentProperties() failed for property " << rLabel.first);
+ }
+ }
+ }
+}
+
+bool SfxClassificationHelper::IsClassified(const uno::Reference<document::XDocumentProperties>& xDocumentProperties)
+{
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+ if (!xPropertyContainer.is())
+ return false;
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
+ for (const beans::Property& rProperty : aProperties)
+ {
+ if (rProperty.Name.startsWith("urn:bails:"))
+ return true;
+ }
+
+ return false;
+}
+
+SfxClassificationCheckPasteResult SfxClassificationHelper::CheckPaste(const uno::Reference<document::XDocumentProperties>& xSource,
+ const uno::Reference<document::XDocumentProperties>& xDestination)
+{
+ if (!SfxClassificationHelper::IsClassified(xSource))
+ // No classification on the source side. Return early, regardless the
+ // state of the destination side.
+ return SfxClassificationCheckPasteResult::None;
+
+ if (!SfxClassificationHelper::IsClassified(xDestination))
+ {
+ // Paste from a classified document to a non-classified one -> deny.
+ return SfxClassificationCheckPasteResult::TargetDocNotClassified;
+ }
+
+ // Remaining case: paste between two classified documents.
+ SfxClassificationHelper aSource(xSource);
+ SfxClassificationHelper aDestination(xDestination);
+ if (aSource.GetImpactScale() != aDestination.GetImpactScale())
+ // It's possible to compare them if they have the same scale.
+ return SfxClassificationCheckPasteResult::None;
+
+ if (aSource.GetImpactLevel() > aDestination.GetImpactLevel())
+ // Paste from a doc that has higher classification -> deny.
+ return SfxClassificationCheckPasteResult::DocClassificationTooLow;
+
+ return SfxClassificationCheckPasteResult::None;
+}
+
+bool SfxClassificationHelper::ShowPasteInfo(SfxClassificationCheckPasteResult eResult)
+{
+ switch (eResult)
+ {
+ case SfxClassificationCheckPasteResult::None:
+ {
+ return true;
+ }
+ break;
+ case SfxClassificationCheckPasteResult::TargetDocNotClassified:
+ {
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_TARGET_DOC_NOT_CLASSIFIED)));
+ xBox->run();
+ }
+ return false;
+ }
+ break;
+ case SfxClassificationCheckPasteResult::DocClassificationTooLow:
+ {
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_DOC_CLASSIFICATION_TOO_LOW)));
+ xBox->run();
+ }
+ return false;
+ }
+ break;
+ }
+
+ return true;
+}
+
+SfxClassificationHelper::SfxClassificationHelper(const uno::Reference<document::XDocumentProperties>& xDocumentProperties, bool bUseLocalizedPolicy)
+ : m_pImpl(std::make_unique<Impl>(xDocumentProperties, bUseLocalizedPolicy))
+{
+ if (!xDocumentProperties.is())
+ return;
+
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+ if (!xPropertyContainer.is())
+ return;
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
+ for (const beans::Property& rProperty : aProperties)
+ {
+ if (!rProperty.Name.startsWith("urn:bails:"))
+ continue;
+
+ uno::Any aAny = xPropertySet->getPropertyValue(rProperty.Name);
+ OUString aValue;
+ if (aAny >>= aValue)
+ {
+ SfxClassificationPolicyType eType = stringToPolicyType(rProperty.Name);
+ OUString aPrefix = policyTypeToString(eType);
+ if (!rProperty.Name.startsWith(aPrefix))
+ // It's a prefix we did not recognize, ignore.
+ continue;
+
+ //TODO: Support abbreviated names(?)
+ if (rProperty.Name == OUStringConcatenation(aPrefix + PROP_BACNAME()))
+ m_pImpl->m_aCategory[eType].m_aName = aValue;
+ else
+ m_pImpl->m_aCategory[eType].m_aLabels[rProperty.Name] = aValue;
+ }
+ }
+}
+
+SfxClassificationHelper::~SfxClassificationHelper() = default;
+
+std::vector<OUString> const & SfxClassificationHelper::GetMarkings() const
+{
+ return m_pImpl->m_aMarkings;
+}
+
+std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyParts() const
+{
+ return m_pImpl->m_aIPParts;
+}
+
+std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyPartNumbers() const
+{
+ return m_pImpl->m_aIPPartNumbers;
+}
+
+const OUString& SfxClassificationHelper::GetBACName(SfxClassificationPolicyType eType) const
+{
+ return m_pImpl->m_aCategory[eType].m_aName;
+}
+
+const OUString& SfxClassificationHelper::GetAbbreviatedBACName(const OUString& sFullName)
+{
+ for (const auto& category : m_pImpl->m_aCategories)
+ {
+ if (category.m_aName == sFullName)
+ return category.m_aAbbreviatedName;
+ }
+
+ return sFullName;
+}
+
+OUString SfxClassificationHelper::GetBACNameForIdentifier(std::u16string_view sIdentifier)
+{
+ if (sIdentifier.empty())
+ return "";
+
+ for (const auto& category : m_pImpl->m_aCategories)
+ {
+ if (category.m_aIdentifier == sIdentifier)
+ return category.m_aName;
+ }
+
+ return "";
+}
+
+OUString SfxClassificationHelper::GetHigherClass(const OUString& first, const OUString& second)
+{
+ size_t nFirstConfidentiality = 0;
+ size_t nSecondConfidentiality = 0;
+ for (const auto& category : m_pImpl->m_aCategories)
+ {
+ if (category.m_aName == first)
+ nFirstConfidentiality = category.m_nConfidentiality;
+ if (category.m_aName == second)
+ nSecondConfidentiality = category.m_nConfidentiality;
+ }
+
+ return nFirstConfidentiality >= nSecondConfidentiality ? first : second;
+}
+
+bool SfxClassificationHelper::HasImpactLevel()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return false;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it == rCategory.m_aLabels.end())
+ return false;
+
+ it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
+ return it != rCategory.m_aLabels.end();
+}
+
+bool SfxClassificationHelper::HasDocumentHeader()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return false;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCHEADER());
+ return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
+}
+
+bool SfxClassificationHelper::HasDocumentFooter()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return false;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCFOOTER());
+ return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
+}
+
+InfobarType SfxClassificationHelper::GetImpactLevelType()
+{
+ InfobarType aRet;
+
+ aRet = InfobarType::WARNING;
+
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return aRet;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it == rCategory.m_aLabels.end())
+ return aRet;
+ OUString aScale = it->second;
+
+ it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
+ if (it == rCategory.m_aLabels.end())
+ return aRet;
+ OUString aLevel = it->second;
+
+ // The spec defines two valid scale values: FIPS-199 and UK-Cabinet.
+ if (aScale == "UK-Cabinet")
+ {
+ if (aLevel == "0")
+ aRet = InfobarType::SUCCESS;
+ else if (aLevel == "1")
+ aRet = InfobarType::WARNING;
+ else if (aLevel == "2")
+ aRet = InfobarType::WARNING;
+ else if (aLevel == "3")
+ aRet = InfobarType::DANGER;
+ }
+ else if (aScale == "FIPS-199")
+ {
+ if (aLevel == "Low")
+ aRet = InfobarType::SUCCESS;
+ else if (aLevel == "Moderate")
+ aRet = InfobarType::WARNING;
+ else if (aLevel == "High")
+ aRet = InfobarType::DANGER;
+ }
+ return aRet;
+}
+
+sal_Int32 SfxClassificationHelper::GetImpactLevel()
+{
+ sal_Int32 nRet = -1;
+
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return nRet;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it == rCategory.m_aLabels.end())
+ return nRet;
+ OUString aScale = it->second;
+
+ it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
+ if (it == rCategory.m_aLabels.end())
+ return nRet;
+ OUString aLevel = it->second;
+
+ if (aScale == "UK-Cabinet")
+ {
+ sal_Int32 nValue = aLevel.toInt32();
+ if (nValue < 0 || nValue > 3)
+ return nRet;
+ nRet = nValue;
+ }
+ else if (aScale == "FIPS-199")
+ {
+ static std::map<OUString, sal_Int32> const aValues
+ {
+ { "Low", 0 },
+ { "Moderate", 1 },
+ { "High", 2 }
+ };
+ auto itValues = aValues.find(aLevel);
+ if (itValues == aValues.end())
+ return nRet;
+ nRet = itValues->second;
+ }
+
+ return nRet;
+}
+
+OUString SfxClassificationHelper::GetImpactScale()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return OUString();
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it != rCategory.m_aLabels.end())
+ return it->second;
+
+ return OUString();
+}
+
+OUString SfxClassificationHelper::GetDocumentWatermark()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return OUString();
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCWATERMARK());
+ if (it != rCategory.m_aLabels.end())
+ return it->second;
+
+ return OUString();
+}
+
+std::vector<OUString> SfxClassificationHelper::GetBACNames()
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ std::vector<OUString> aRet;
+ std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aName;
+ });
+ return aRet;
+}
+
+std::vector<OUString> SfxClassificationHelper::GetBACIdentifiers()
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ std::vector<OUString> aRet;
+ std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aIdentifier;
+ });
+ return aRet;
+}
+
+std::vector<OUString> SfxClassificationHelper::GetAbbreviatedBACNames()
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ std::vector<OUString> aRet;
+ std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aAbbreviatedName;
+ });
+ return aRet;
+}
+
+void SfxClassificationHelper::SetBACName(const OUString& rName, SfxClassificationPolicyType eType)
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ auto it = std::find_if(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), [&](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aName == rName;
+ });
+ if (it == m_pImpl->m_aCategories.end())
+ {
+ SAL_WARN("sfx.view", "'" << rName << "' is not a recognized category name");
+ return;
+ }
+
+ m_pImpl->m_aCategory[eType].m_aName = it->m_aName;
+ m_pImpl->m_aCategory[eType].m_aAbbreviatedName = it->m_aAbbreviatedName;
+ m_pImpl->m_aCategory[eType].m_nConfidentiality = it->m_nConfidentiality;
+ m_pImpl->m_aCategory[eType].m_aLabels.clear();
+ const OUString& rPrefix = policyTypeToString(eType);
+ for (const auto& rLabel : it->m_aLabels)
+ m_pImpl->m_aCategory[eType].m_aLabels[rPrefix + rLabel.first] = rLabel.second;
+
+ m_pImpl->setStartValidity(eType);
+ m_pImpl->pushToDocumentProperties();
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (!pViewFrame)
+ return;
+
+ UpdateInfobar(*pViewFrame);
+}
+
+void SfxClassificationHelper::UpdateInfobar(SfxViewFrame& rViewFrame)
+{
+ OUString aBACName = GetBACName(SfxClassificationPolicyType::IntellectualProperty);
+ bool bImpactLevel = HasImpactLevel();
+ if (!aBACName.isEmpty() && bImpactLevel)
+ {
+ OUString aMessage = SfxResId(STR_CLASSIFIED_DOCUMENT);
+ aMessage = aMessage.replaceFirst("%1", aBACName);
+
+ rViewFrame.RemoveInfoBar(u"classification");
+ rViewFrame.AppendInfoBar("classification", "", aMessage, GetImpactLevelType());
+ }
+}
+
+SfxClassificationPolicyType SfxClassificationHelper::stringToPolicyType(std::u16string_view rType)
+{
+ if (o3tl::starts_with(rType, PROP_PREFIX_EXPORTCONTROL()))
+ return SfxClassificationPolicyType::ExportControl;
+ else if (o3tl::starts_with(rType, PROP_PREFIX_NATIONALSECURITY()))
+ return SfxClassificationPolicyType::NationalSecurity;
+ else
+ return SfxClassificationPolicyType::IntellectualProperty;
+}
+
+const OUString& SfxClassificationHelper::policyTypeToString(SfxClassificationPolicyType eType)
+{
+ switch (eType)
+ {
+ case SfxClassificationPolicyType::ExportControl:
+ return PROP_PREFIX_EXPORTCONTROL();
+ case SfxClassificationPolicyType::NationalSecurity:
+ return PROP_PREFIX_NATIONALSECURITY();
+ case SfxClassificationPolicyType::IntellectualProperty:
+ break;
+ }
+
+ return PROP_PREFIX_INTELLECTUALPROPERTY();
+}
+
+const OUString& SfxClassificationHelper::PROP_DOCHEADER()
+{
+ static const OUString sProp("Marking:document-header");
+ return sProp;
+}
+
+const OUString& SfxClassificationHelper::PROP_DOCFOOTER()
+{
+ static const OUString sProp("Marking:document-footer");
+ return sProp;
+}
+
+const OUString& SfxClassificationHelper::PROP_DOCWATERMARK()
+{
+ static const OUString sProp("Marking:document-watermark");
+ return sProp;
+}
+
+const OUString& SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY()
+{
+ static const OUString sProp("urn:bails:IntellectualProperty:");
+ return sProp;
+}
+
+SfxClassificationPolicyType SfxClassificationHelper::getPolicyType()
+{
+ sal_Int32 nPolicyTypeNumber = officecfg::Office::Common::Classification::Policy::get();
+ auto eType = static_cast<SfxClassificationPolicyType>(nPolicyTypeNumber);
+ return eType;
+}
+
+namespace sfx
+{
+
+namespace
+{
+
+OUString getProperty(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer,
+ OUString const& rName)
+{
+ try
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
+ return xPropertySet->getPropertyValue(rName).get<OUString>();
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ return OUString();
+}
+
+} // end anonymous namespace
+
+sfx::ClassificationCreationOrigin getCreationOriginProperty(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer,
+ sfx::ClassificationKeyCreator const & rKeyCreator)
+{
+ OUString sValue = getProperty(rxPropertyContainer, rKeyCreator.makeCreationOriginKey());
+ if (sValue.isEmpty())
+ return sfx::ClassificationCreationOrigin::NONE;
+
+ return (sValue == "BAF_POLICY")
+ ? sfx::ClassificationCreationOrigin::BAF_POLICY
+ : sfx::ClassificationCreationOrigin::MANUAL;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */