Use correct object
[LibreOffice.git] / sfx2 / source / view / classificationhelper.cxx
blobb74f2486d2a9474063413a290534ddf86d3f9999
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sfx2/classificationhelper.hxx>
12 #include <map>
13 #include <algorithm>
14 #include <iterator>
16 #include <com/sun/star/beans/XPropertyContainer.hpp>
17 #include <com/sun/star/beans/Property.hpp>
18 #include <com/sun/star/beans/XPropertySet.hpp>
19 #include <com/sun/star/document/XDocumentProperties.hpp>
20 #include <com/sun/star/xml/sax/Parser.hpp>
21 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
22 #include <com/sun/star/xml/sax/SAXParseException.hpp>
23 #include <com/sun/star/beans/PropertyAttribute.hpp>
25 #include <sal/log.hxx>
26 #include <i18nlangtag/languagetag.hxx>
27 #include <sfx2/infobar.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <unotools/configmgr.hxx>
30 #include <unotools/pathoptions.hxx>
31 #include <unotools/ucbstreamhelper.hxx>
32 #include <unotools/streamwrap.hxx>
33 #include <cppuhelper/implbase.hxx>
34 #include <sfx2/strings.hrc>
35 #include <sfx2/sfxresid.hxx>
36 #include <sfx2/viewfrm.hxx>
37 #include <tools/datetime.hxx>
38 #include <comphelper/diagnose_ex.hxx>
39 #include <unotools/datetime.hxx>
40 #include <vcl/svapp.hxx>
41 #include <vcl/settings.hxx>
42 #include <vcl/weld.hxx>
43 #include <svl/fstathelper.hxx>
45 #include <o3tl/string_view.hxx>
46 #include <officecfg/Office/Common.hxx>
48 using namespace com::sun::star;
50 namespace
53 const OUString& PROP_BACNAME()
55 static constexpr OUString sProp(u"BusinessAuthorizationCategory:Name"_ustr);
56 return sProp;
59 const OUString& PROP_STARTVALIDITY()
61 static constexpr OUString sProp(u"Authorization:StartValidity"_ustr);
62 return sProp;
65 const OUString& PROP_NONE()
67 static constexpr OUString sProp(u"None"_ustr);
68 return sProp;
71 const OUString& PROP_IMPACTSCALE()
73 static constexpr OUString sProp(u"Impact:Scale"_ustr);
74 return sProp;
77 const OUString& PROP_IMPACTLEVEL()
79 static constexpr OUString sProp(u"Impact:Level:Confidentiality"_ustr);
80 return sProp;
83 const OUString& PROP_PREFIX_EXPORTCONTROL()
85 static constexpr OUString sProp(u"urn:bails:ExportControl:"_ustr);
86 return sProp;
89 const OUString& PROP_PREFIX_NATIONALSECURITY()
91 static constexpr OUString sProp(u"urn:bails:NationalSecurity:"_ustr);
92 return sProp;
95 /// Represents one category of a classification policy.
96 class SfxClassificationCategory
98 public:
99 /// PROP_BACNAME() is stored separately for easier lookup.
100 OUString m_aName;
101 OUString m_aAbbreviatedName; //< An abbreviation to display instead of m_aName.
102 OUString m_aIdentifier; //< The Identifier of this entry.
103 size_t m_nConfidentiality; //< 0 is the lowest (least-sensitive).
104 std::map<OUString, OUString> m_aLabels;
107 /// Parses a policy XML conforming to the TSCP BAF schema.
108 class SfxClassificationParser : public cppu::WeakImplHelper<xml::sax::XDocumentHandler>
110 public:
111 std::vector<SfxClassificationCategory> m_aCategories;
112 std::vector<OUString> m_aMarkings;
113 std::vector<OUString> m_aIPParts;
114 std::vector<OUString> m_aIPPartNumbers;
116 OUString m_aPolicyAuthorityName;
117 bool m_bInPolicyAuthorityName = false;
118 OUString m_aPolicyName;
119 bool m_bInPolicyName = false;
120 OUString m_aProgramID;
121 bool m_bInProgramID = false;
122 OUString m_aScale;
123 bool m_bInScale = false;
124 OUString m_aConfidentalityValue;
125 bool m_bInConfidentalityValue = false;
126 OUString m_aIdentifier;
127 bool m_bInIdentifier = false;
128 OUString m_aValue;
129 bool m_bInValue = false;
131 /// Pointer to a value in m_aCategories, the currently parsed category.
132 SfxClassificationCategory* m_pCategory = nullptr;
134 SfxClassificationParser();
136 void SAL_CALL startDocument() override;
138 void SAL_CALL endDocument() override;
140 void SAL_CALL startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs) override;
142 void SAL_CALL endElement(const OUString& rName) override;
144 void SAL_CALL characters(const OUString& rChars) override;
146 void SAL_CALL ignorableWhitespace(const OUString& rWhitespaces) override;
148 void SAL_CALL processingInstruction(const OUString& rTarget, const OUString& rData) override;
150 void SAL_CALL setDocumentLocator(const uno::Reference<xml::sax::XLocator>& xLocator) override;
153 SfxClassificationParser::SfxClassificationParser() = default;
155 void SAL_CALL SfxClassificationParser::startDocument()
159 void SAL_CALL SfxClassificationParser::endDocument()
163 void SAL_CALL SfxClassificationParser::startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs)
165 if (rName == "baf:PolicyAuthorityName")
167 m_aPolicyAuthorityName.clear();
168 m_bInPolicyAuthorityName = true;
170 else if (rName == "baf:PolicyName")
172 m_aPolicyName.clear();
173 m_bInPolicyName = true;
175 else if (rName == "baf:ProgramID")
177 m_aProgramID.clear();
178 m_bInProgramID = true;
180 else if (rName == "baf:BusinessAuthorizationCategory")
182 const OUString aName = xAttribs->getValueByName(u"Name"_ustr);
183 if (!m_pCategory && !aName.isEmpty())
185 OUString aIdentifier = xAttribs->getValueByName(u"Identifier"_ustr);
187 // Create a new category and initialize it with the data that's true for all categories.
188 m_aCategories.emplace_back();
189 SfxClassificationCategory& rCategory = m_aCategories.back();
191 rCategory.m_aName = aName;
192 // Set the abbreviated name, if any, otherwise fallback on the full name.
193 const OUString aAbbreviatedName = xAttribs->getValueByName(u"loextAbbreviatedName"_ustr);
194 rCategory.m_aAbbreviatedName = !aAbbreviatedName.isEmpty() ? aAbbreviatedName : aName;
195 rCategory.m_aIdentifier = aIdentifier;
197 rCategory.m_aLabels[u"PolicyAuthority:Name"_ustr] = m_aPolicyAuthorityName;
198 rCategory.m_aLabels[u"Policy:Name"_ustr] = m_aPolicyName;
199 rCategory.m_aLabels[u"BusinessAuthorization:Identifier"_ustr] = m_aProgramID;
200 rCategory.m_aLabels[u"BusinessAuthorizationCategory:Identifier"_ustr] = aIdentifier;
202 // Also initialize defaults.
203 rCategory.m_aLabels[u"PolicyAuthority:Identifier"_ustr] = PROP_NONE();
204 rCategory.m_aLabels[u"PolicyAuthority:Country"_ustr] = PROP_NONE();
205 rCategory.m_aLabels[u"Policy:Identifier"_ustr] = PROP_NONE();
206 rCategory.m_aLabels[u"BusinessAuthorization:Name"_ustr] = PROP_NONE();
207 rCategory.m_aLabels[u"BusinessAuthorization:Locator"_ustr] = PROP_NONE();
208 rCategory.m_aLabels[u"BusinessAuthorizationCategory:Identifier:OID"_ustr] = PROP_NONE();
209 rCategory.m_aLabels[u"BusinessAuthorizationCategory:Locator"_ustr] = PROP_NONE();
210 rCategory.m_aLabels[u"BusinessAuthorization:Locator"_ustr] = PROP_NONE();
211 rCategory.m_aLabels[u"MarkingPrecedence"_ustr] = PROP_NONE();
212 rCategory.m_aLabels[u"Marking:general-summary"_ustr].clear();
213 rCategory.m_aLabels[u"Marking:general-warning-statement"_ustr].clear();
214 rCategory.m_aLabels[u"Marking:general-warning-statement:ext:2"_ustr].clear();
215 rCategory.m_aLabels[u"Marking:general-warning-statement:ext:3"_ustr].clear();
216 rCategory.m_aLabels[u"Marking:general-warning-statement:ext:4"_ustr].clear();
217 rCategory.m_aLabels[u"Marking:general-distribution-statement"_ustr].clear();
218 rCategory.m_aLabels[u"Marking:general-distribution-statement:ext:2"_ustr].clear();
219 rCategory.m_aLabels[u"Marking:general-distribution-statement:ext:3"_ustr].clear();
220 rCategory.m_aLabels[u"Marking:general-distribution-statement:ext:4"_ustr].clear();
221 rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCHEADER()].clear();
222 rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCFOOTER()].clear();
223 rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCWATERMARK()].clear();
224 rCategory.m_aLabels[u"Marking:email-first-line-of-text"_ustr].clear();
225 rCategory.m_aLabels[u"Marking:email-last-line-of-text"_ustr].clear();
226 rCategory.m_aLabels[u"Marking:email-subject-prefix"_ustr].clear();
227 rCategory.m_aLabels[u"Marking:email-subject-suffix"_ustr].clear();
228 rCategory.m_aLabels[PROP_STARTVALIDITY()] = PROP_NONE();
229 rCategory.m_aLabels[u"Authorization:StopValidity"_ustr] = PROP_NONE();
230 m_pCategory = &rCategory;
233 else if (rName == "loext:Marking")
235 OUString aName = xAttribs->getValueByName(u"Name"_ustr);
236 m_aMarkings.push_back(aName);
238 else if (rName == "loext:IntellectualPropertyPart")
240 OUString aName = xAttribs->getValueByName(u"Name"_ustr);
241 m_aIPParts.push_back(aName);
243 else if (rName == "loext:IntellectualPropertyPartNumber")
245 OUString aName = xAttribs->getValueByName(u"Name"_ustr);
246 m_aIPPartNumbers.push_back(aName);
248 else if (rName == "baf:Scale")
250 m_aScale.clear();
251 m_bInScale = true;
253 else if (rName == "baf:ConfidentalityValue")
255 m_aConfidentalityValue.clear();
256 m_bInConfidentalityValue = true;
258 else if (rName == "baf:Identifier")
260 m_aIdentifier.clear();
261 m_bInIdentifier = true;
263 else if (rName == "baf:Value")
265 m_aValue.clear();
266 m_bInValue = true;
270 void SAL_CALL SfxClassificationParser::endElement(const OUString& rName)
272 if (rName == "baf:PolicyAuthorityName")
273 m_bInPolicyAuthorityName = false;
274 else if (rName == "baf:PolicyName")
275 m_bInPolicyName = false;
276 else if (rName == "baf:ProgramID")
277 m_bInProgramID = false;
278 else if (rName == "baf:BusinessAuthorizationCategory")
279 m_pCategory = nullptr;
280 else if (rName == "baf:Scale")
282 m_bInScale = false;
283 if (m_pCategory)
284 m_pCategory->m_aLabels[PROP_IMPACTSCALE()] = m_aScale;
286 else if (rName == "baf:ConfidentalityValue")
288 m_bInConfidentalityValue = false;
289 if (m_pCategory)
291 std::map<OUString, OUString>& rLabels = m_pCategory->m_aLabels;
292 rLabels[PROP_IMPACTLEVEL()] = m_aConfidentalityValue;
293 m_pCategory->m_nConfidentiality = m_aConfidentalityValue.toInt32(); // 0-based class sensitivity; 0 is lowest.
294 // Set the two other type of levels as well, if they're not set
295 // yet: they're optional in BAF, but not in BAILS.
296 rLabels.try_emplace(u"Impact:Level:Integrity"_ustr, m_aConfidentalityValue);
297 rLabels.try_emplace(u"Impact:Level:Availability"_ustr, m_aConfidentalityValue);
300 else if (rName == "baf:Identifier")
301 m_bInIdentifier = false;
302 else if (rName == "baf:Value")
304 if (m_pCategory)
306 if (m_aIdentifier == "Document: Header")
307 m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCHEADER()] = m_aValue;
308 else if (m_aIdentifier == "Document: Footer")
309 m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCFOOTER()] = m_aValue;
310 else if (m_aIdentifier == "Document: Watermark")
311 m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCWATERMARK()] = m_aValue;
316 void SAL_CALL SfxClassificationParser::characters(const OUString& rChars)
318 if (m_bInPolicyAuthorityName)
319 m_aPolicyAuthorityName += rChars;
320 else if (m_bInPolicyName)
321 m_aPolicyName += rChars;
322 else if (m_bInProgramID)
323 m_aProgramID += rChars;
324 else if (m_bInScale)
325 m_aScale += rChars;
326 else if (m_bInConfidentalityValue)
327 m_aConfidentalityValue += rChars;
328 else if (m_bInIdentifier)
329 m_aIdentifier += rChars;
330 else if (m_bInValue)
331 m_aValue += rChars;
334 void SAL_CALL SfxClassificationParser::ignorableWhitespace(const OUString& /*rWhitespace*/)
338 void SAL_CALL SfxClassificationParser::processingInstruction(const OUString& /*rTarget*/, const OUString& /*rData*/)
342 void SAL_CALL SfxClassificationParser::setDocumentLocator(const uno::Reference<xml::sax::XLocator>& /*xLocator*/)
346 } // anonymous namespace
348 /// Implementation details of SfxClassificationHelper.
349 class SfxClassificationHelper::Impl
351 public:
352 /// Selected categories, one category for each policy type.
353 std::map<SfxClassificationPolicyType, SfxClassificationCategory> m_aCategory;
354 /// Possible categories of a policy to choose from.
355 std::vector<SfxClassificationCategory> m_aCategories;
356 std::vector<OUString> m_aMarkings;
357 std::vector<OUString> m_aIPParts;
358 std::vector<OUString> m_aIPPartNumbers;
360 uno::Reference<document::XDocumentProperties> m_xDocumentProperties;
362 bool m_bUseLocalized;
364 explicit Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized);
365 void parsePolicy();
366 /// Synchronize m_aLabels back to the document properties.
367 void pushToDocumentProperties();
368 /// Set the classification start date to the system time.
369 void setStartValidity(SfxClassificationPolicyType eType);
372 SfxClassificationHelper::Impl::Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized)
373 : m_xDocumentProperties(std::move(xDocumentProperties))
374 , m_bUseLocalized(bUseLocalized)
376 parsePolicy();
379 void SfxClassificationHelper::Impl::parsePolicy()
381 const uno::Reference<uno::XComponentContext>& xComponentContext = comphelper::getProcessComponentContext();
382 SvtPathOptions aOptions;
383 OUString aPath = aOptions.GetClassificationPath();
385 // See if there is a localized variant next to the configured XML.
386 OUString aExtension(u".xml"_ustr);
387 if (aPath.endsWith(aExtension) && m_bUseLocalized)
389 std::u16string_view aBase = aPath.subView(0, aPath.getLength() - aExtension.getLength());
390 const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
391 // Expected format is "<original path>_xx-XX.xml".
392 OUString aLocalized = OUString::Concat(aBase) + "_" + rLanguageTag.getBcp47() + aExtension;
393 if (FStatHelper::IsDocument(aLocalized))
394 aPath = aLocalized;
397 xml::sax::InputSource aParserInput;
398 std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(aPath, StreamMode::READ);
399 aParserInput.aInputStream.set(new utl::OStreamWrapper(std::move(pStream)));
401 uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xComponentContext);
402 rtl::Reference<SfxClassificationParser> xClassificationParser(new SfxClassificationParser());
403 xParser->setDocumentHandler(xClassificationParser);
406 xParser->parseStream(aParserInput);
408 catch (const xml::sax::SAXParseException&)
410 TOOLS_WARN_EXCEPTION("sfx.view", "parsePolicy() failed");
412 m_aCategories = xClassificationParser->m_aCategories;
413 m_aMarkings = xClassificationParser->m_aMarkings;
414 m_aIPParts = xClassificationParser->m_aIPParts;
415 m_aIPPartNumbers = xClassificationParser->m_aIPPartNumbers;
418 static bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties, std::u16string_view rName)
420 return std::any_of(rProperties.begin(), rProperties.end(), [&](const beans::Property& rProperty)
422 return rProperty.Name == rName;
426 void SfxClassificationHelper::Impl::setStartValidity(SfxClassificationPolicyType eType)
428 auto itCategory = m_aCategory.find(eType);
429 if (itCategory == m_aCategory.end())
430 return;
432 SfxClassificationCategory& rCategory = itCategory->second;
433 auto it = rCategory.m_aLabels.find(policyTypeToString(eType) + PROP_STARTVALIDITY());
434 if (it != rCategory.m_aLabels.end())
436 if (it->second == PROP_NONE())
438 // The policy left the start date unchanged, replace it with the system time.
439 util::DateTime aDateTime = DateTime(DateTime::SYSTEM).GetUNODateTime();
440 it->second = utl::toISO8601(aDateTime);
445 void SfxClassificationHelper::Impl::pushToDocumentProperties()
447 uno::Reference<beans::XPropertyContainer> xPropertyContainer = m_xDocumentProperties->getUserDefinedProperties();
448 uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
449 uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
450 for (auto& rPair : m_aCategory)
452 SfxClassificationPolicyType eType = rPair.first;
453 SfxClassificationCategory& rCategory = rPair.second;
454 std::map<OUString, OUString> aLabels = rCategory.m_aLabels;
455 aLabels[policyTypeToString(eType) + PROP_BACNAME()] = rCategory.m_aName;
456 for (const auto& rLabel : aLabels)
460 if (lcl_containsProperty(aProperties, rLabel.first))
461 xPropertySet->setPropertyValue(rLabel.first, uno::Any(rLabel.second));
462 else
463 xPropertyContainer->addProperty(rLabel.first, beans::PropertyAttribute::REMOVABLE, uno::Any(rLabel.second));
465 catch (const uno::Exception&)
467 TOOLS_WARN_EXCEPTION("sfx.view", "pushDocumentProperties() failed for property " << rLabel.first);
473 bool SfxClassificationHelper::IsClassified(const uno::Reference<document::XDocumentProperties>& xDocumentProperties)
475 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
476 if (!xPropertyContainer.is())
477 return false;
479 uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
480 const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
481 for (const beans::Property& rProperty : aProperties)
483 if (rProperty.Name.startsWith("urn:bails:"))
484 return true;
487 return false;
490 SfxClassificationCheckPasteResult SfxClassificationHelper::CheckPaste(const uno::Reference<document::XDocumentProperties>& xSource,
491 const uno::Reference<document::XDocumentProperties>& xDestination)
493 if (!SfxClassificationHelper::IsClassified(xSource))
494 // No classification on the source side. Return early, regardless the
495 // state of the destination side.
496 return SfxClassificationCheckPasteResult::None;
498 if (!SfxClassificationHelper::IsClassified(xDestination))
500 // Paste from a classified document to a non-classified one -> deny.
501 return SfxClassificationCheckPasteResult::TargetDocNotClassified;
504 // Remaining case: paste between two classified documents.
505 SfxClassificationHelper aSource(xSource);
506 SfxClassificationHelper aDestination(xDestination);
507 if (aSource.GetImpactScale() != aDestination.GetImpactScale())
508 // It's possible to compare them if they have the same scale.
509 return SfxClassificationCheckPasteResult::None;
511 if (aSource.GetImpactLevel() > aDestination.GetImpactLevel())
512 // Paste from a doc that has higher classification -> deny.
513 return SfxClassificationCheckPasteResult::DocClassificationTooLow;
515 return SfxClassificationCheckPasteResult::None;
518 bool SfxClassificationHelper::ShowPasteInfo(SfxClassificationCheckPasteResult eResult)
520 switch (eResult)
522 case SfxClassificationCheckPasteResult::None:
524 return true;
526 break;
527 case SfxClassificationCheckPasteResult::TargetDocNotClassified:
529 if (!Application::IsHeadlessModeEnabled())
531 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
532 VclMessageType::Info, VclButtonsType::Ok,
533 SfxResId(STR_TARGET_DOC_NOT_CLASSIFIED)));
534 xBox->run();
536 return false;
538 break;
539 case SfxClassificationCheckPasteResult::DocClassificationTooLow:
541 if (!Application::IsHeadlessModeEnabled())
543 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
544 VclMessageType::Info, VclButtonsType::Ok,
545 SfxResId(STR_DOC_CLASSIFICATION_TOO_LOW)));
546 xBox->run();
548 return false;
550 break;
553 return true;
556 SfxClassificationHelper::SfxClassificationHelper(const uno::Reference<document::XDocumentProperties>& xDocumentProperties, bool bUseLocalizedPolicy)
557 : m_pImpl(std::make_unique<Impl>(xDocumentProperties, bUseLocalizedPolicy))
559 if (!xDocumentProperties.is())
560 return;
562 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
563 if (!xPropertyContainer.is())
564 return;
566 uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
567 const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
568 for (const beans::Property& rProperty : aProperties)
570 if (!rProperty.Name.startsWith("urn:bails:"))
571 continue;
573 uno::Any aAny = xPropertySet->getPropertyValue(rProperty.Name);
574 OUString aValue;
575 if (aAny >>= aValue)
577 SfxClassificationPolicyType eType = stringToPolicyType(rProperty.Name);
578 OUString aPrefix = policyTypeToString(eType);
579 if (!rProperty.Name.startsWith(aPrefix))
580 // It's a prefix we did not recognize, ignore.
581 continue;
583 //TODO: Support abbreviated names(?)
584 if (rProperty.Name == Concat2View(aPrefix + PROP_BACNAME()))
585 m_pImpl->m_aCategory[eType].m_aName = aValue;
586 else
587 m_pImpl->m_aCategory[eType].m_aLabels[rProperty.Name] = aValue;
592 SfxClassificationHelper::~SfxClassificationHelper() = default;
594 std::vector<OUString> const & SfxClassificationHelper::GetMarkings() const
596 return m_pImpl->m_aMarkings;
599 std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyParts() const
601 return m_pImpl->m_aIPParts;
604 std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyPartNumbers() const
606 return m_pImpl->m_aIPPartNumbers;
609 const OUString& SfxClassificationHelper::GetBACName(SfxClassificationPolicyType eType) const
611 return m_pImpl->m_aCategory[eType].m_aName;
614 const OUString& SfxClassificationHelper::GetAbbreviatedBACName(const OUString& sFullName)
616 for (const auto& category : m_pImpl->m_aCategories)
618 if (category.m_aName == sFullName)
619 return category.m_aAbbreviatedName;
622 return sFullName;
625 OUString SfxClassificationHelper::GetBACNameForIdentifier(std::u16string_view sIdentifier)
627 if (sIdentifier.empty())
628 return u""_ustr;
630 for (const auto& category : m_pImpl->m_aCategories)
632 if (category.m_aIdentifier == sIdentifier)
633 return category.m_aName;
636 return u""_ustr;
639 OUString SfxClassificationHelper::GetHigherClass(const OUString& first, const OUString& second)
641 size_t nFirstConfidentiality = 0;
642 size_t nSecondConfidentiality = 0;
643 for (const auto& category : m_pImpl->m_aCategories)
645 if (category.m_aName == first)
646 nFirstConfidentiality = category.m_nConfidentiality;
647 if (category.m_aName == second)
648 nSecondConfidentiality = category.m_nConfidentiality;
651 return nFirstConfidentiality >= nSecondConfidentiality ? first : second;
654 bool SfxClassificationHelper::HasImpactLevel()
656 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
657 if (itCategory == m_pImpl->m_aCategory.end())
658 return false;
660 SfxClassificationCategory& rCategory = itCategory->second;
661 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
662 if (it == rCategory.m_aLabels.end())
663 return false;
665 it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
666 return it != rCategory.m_aLabels.end();
669 bool SfxClassificationHelper::HasDocumentHeader()
671 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
672 if (itCategory == m_pImpl->m_aCategory.end())
673 return false;
675 SfxClassificationCategory& rCategory = itCategory->second;
676 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCHEADER());
677 return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
680 bool SfxClassificationHelper::HasDocumentFooter()
682 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
683 if (itCategory == m_pImpl->m_aCategory.end())
684 return false;
686 SfxClassificationCategory& rCategory = itCategory->second;
687 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCFOOTER());
688 return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
691 InfobarType SfxClassificationHelper::GetImpactLevelType()
693 InfobarType aRet;
695 aRet = InfobarType::WARNING;
697 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
698 if (itCategory == m_pImpl->m_aCategory.end())
699 return aRet;
701 SfxClassificationCategory& rCategory = itCategory->second;
702 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
703 if (it == rCategory.m_aLabels.end())
704 return aRet;
705 OUString aScale = it->second;
707 it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
708 if (it == rCategory.m_aLabels.end())
709 return aRet;
710 OUString aLevel = it->second;
712 // The spec defines two valid scale values: FIPS-199 and UK-Cabinet.
713 if (aScale == "UK-Cabinet")
715 if (aLevel == "0")
716 aRet = InfobarType::SUCCESS;
717 else if (aLevel == "1")
718 aRet = InfobarType::WARNING;
719 else if (aLevel == "2")
720 aRet = InfobarType::WARNING;
721 else if (aLevel == "3")
722 aRet = InfobarType::DANGER;
724 else if (aScale == "FIPS-199")
726 if (aLevel == "Low")
727 aRet = InfobarType::SUCCESS;
728 else if (aLevel == "Moderate")
729 aRet = InfobarType::WARNING;
730 else if (aLevel == "High")
731 aRet = InfobarType::DANGER;
733 return aRet;
736 sal_Int32 SfxClassificationHelper::GetImpactLevel()
738 sal_Int32 nRet = -1;
740 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
741 if (itCategory == m_pImpl->m_aCategory.end())
742 return nRet;
744 SfxClassificationCategory& rCategory = itCategory->second;
745 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
746 if (it == rCategory.m_aLabels.end())
747 return nRet;
748 OUString aScale = it->second;
750 it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
751 if (it == rCategory.m_aLabels.end())
752 return nRet;
753 OUString aLevel = it->second;
755 if (aScale == "UK-Cabinet")
757 sal_Int32 nValue = aLevel.toInt32();
758 if (nValue < 0 || nValue > 3)
759 return nRet;
760 nRet = nValue;
762 else if (aScale == "FIPS-199")
764 static std::map<OUString, sal_Int32> const aValues
766 { "Low", 0 },
767 { "Moderate", 1 },
768 { "High", 2 }
770 auto itValues = aValues.find(aLevel);
771 if (itValues == aValues.end())
772 return nRet;
773 nRet = itValues->second;
776 return nRet;
779 OUString SfxClassificationHelper::GetImpactScale()
781 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
782 if (itCategory == m_pImpl->m_aCategory.end())
783 return OUString();
785 SfxClassificationCategory& rCategory = itCategory->second;
786 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
787 if (it != rCategory.m_aLabels.end())
788 return it->second;
790 return OUString();
793 OUString SfxClassificationHelper::GetDocumentWatermark()
795 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
796 if (itCategory == m_pImpl->m_aCategory.end())
797 return OUString();
799 SfxClassificationCategory& rCategory = itCategory->second;
800 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCWATERMARK());
801 if (it != rCategory.m_aLabels.end())
802 return it->second;
804 return OUString();
807 std::vector<OUString> SfxClassificationHelper::GetBACNames()
809 if (m_pImpl->m_aCategories.empty())
810 m_pImpl->parsePolicy();
812 std::vector<OUString> aRet;
813 std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
815 return rCategory.m_aName;
817 return aRet;
820 std::vector<OUString> SfxClassificationHelper::GetBACIdentifiers()
822 if (m_pImpl->m_aCategories.empty())
823 m_pImpl->parsePolicy();
825 std::vector<OUString> aRet;
826 std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
828 return rCategory.m_aIdentifier;
830 return aRet;
833 std::vector<OUString> SfxClassificationHelper::GetAbbreviatedBACNames()
835 if (m_pImpl->m_aCategories.empty())
836 m_pImpl->parsePolicy();
838 std::vector<OUString> aRet;
839 std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
841 return rCategory.m_aAbbreviatedName;
843 return aRet;
846 void SfxClassificationHelper::SetBACName(const OUString& rName, SfxClassificationPolicyType eType)
848 if (m_pImpl->m_aCategories.empty())
849 m_pImpl->parsePolicy();
851 auto it = std::find_if(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), [&](const SfxClassificationCategory& rCategory)
853 return rCategory.m_aName == rName;
855 if (it == m_pImpl->m_aCategories.end())
857 SAL_WARN("sfx.view", "'" << rName << "' is not a recognized category name");
858 return;
861 m_pImpl->m_aCategory[eType].m_aName = it->m_aName;
862 m_pImpl->m_aCategory[eType].m_aAbbreviatedName = it->m_aAbbreviatedName;
863 m_pImpl->m_aCategory[eType].m_nConfidentiality = it->m_nConfidentiality;
864 m_pImpl->m_aCategory[eType].m_aLabels.clear();
865 const OUString& rPrefix = policyTypeToString(eType);
866 for (const auto& rLabel : it->m_aLabels)
867 m_pImpl->m_aCategory[eType].m_aLabels[rPrefix + rLabel.first] = rLabel.second;
869 m_pImpl->setStartValidity(eType);
870 m_pImpl->pushToDocumentProperties();
871 SfxViewFrame* pViewFrame = SfxViewFrame::Current();
872 if (!pViewFrame)
873 return;
875 UpdateInfobar(*pViewFrame);
878 void SfxClassificationHelper::UpdateInfobar(SfxViewFrame& rViewFrame)
880 OUString aBACName = GetBACName(SfxClassificationPolicyType::IntellectualProperty);
881 bool bImpactLevel = HasImpactLevel();
882 if (!aBACName.isEmpty() && bImpactLevel)
884 OUString aMessage = SfxResId(STR_CLASSIFIED_DOCUMENT);
885 aMessage = aMessage.replaceFirst("%1", aBACName);
887 rViewFrame.RemoveInfoBar(u"classification");
888 rViewFrame.AppendInfoBar(u"classification"_ustr, u""_ustr, aMessage, GetImpactLevelType());
892 SfxClassificationPolicyType SfxClassificationHelper::stringToPolicyType(std::u16string_view rType)
894 if (o3tl::starts_with(rType, PROP_PREFIX_EXPORTCONTROL()))
895 return SfxClassificationPolicyType::ExportControl;
896 else if (o3tl::starts_with(rType, PROP_PREFIX_NATIONALSECURITY()))
897 return SfxClassificationPolicyType::NationalSecurity;
898 else
899 return SfxClassificationPolicyType::IntellectualProperty;
902 const OUString& SfxClassificationHelper::policyTypeToString(SfxClassificationPolicyType eType)
904 switch (eType)
906 case SfxClassificationPolicyType::ExportControl:
907 return PROP_PREFIX_EXPORTCONTROL();
908 case SfxClassificationPolicyType::NationalSecurity:
909 return PROP_PREFIX_NATIONALSECURITY();
910 case SfxClassificationPolicyType::IntellectualProperty:
911 break;
914 return PROP_PREFIX_INTELLECTUALPROPERTY();
917 const OUString& SfxClassificationHelper::PROP_DOCHEADER()
919 static constexpr OUString sProp(u"Marking:document-header"_ustr);
920 return sProp;
923 const OUString& SfxClassificationHelper::PROP_DOCFOOTER()
925 static constexpr OUString sProp(u"Marking:document-footer"_ustr);
926 return sProp;
929 const OUString& SfxClassificationHelper::PROP_DOCWATERMARK()
931 static constexpr OUString sProp(u"Marking:document-watermark"_ustr);
932 return sProp;
935 const OUString& SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY()
937 static constexpr OUString sProp(u"urn:bails:IntellectualProperty:"_ustr);
938 return sProp;
941 SfxClassificationPolicyType SfxClassificationHelper::getPolicyType()
943 if (comphelper::IsFuzzing())
944 return SfxClassificationPolicyType::IntellectualProperty;
945 sal_Int32 nPolicyTypeNumber = officecfg::Office::Common::Classification::Policy::get();
946 auto eType = static_cast<SfxClassificationPolicyType>(nPolicyTypeNumber);
947 return eType;
950 namespace sfx
953 namespace
956 OUString getProperty(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer,
957 OUString const& rName)
961 uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
962 return xPropertySet->getPropertyValue(rName).get<OUString>();
964 catch (const css::uno::Exception&)
968 return OUString();
971 } // end anonymous namespace
973 sfx::ClassificationCreationOrigin getCreationOriginProperty(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer,
974 sfx::ClassificationKeyCreator const & rKeyCreator)
976 OUString sValue = getProperty(rxPropertyContainer, rKeyCreator.makeCreationOriginKey());
977 if (sValue.isEmpty())
978 return sfx::ClassificationCreationOrigin::NONE;
980 return (sValue == "BAF_POLICY")
981 ? sfx::ClassificationCreationOrigin::BAF_POLICY
982 : sfx::ClassificationCreationOrigin::MANUAL;
987 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */