1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10 #include <sfx2/classificationhelper.hxx>
11
12 #include <map>
13 #include <algorithm>
14 #include <iterator>
15
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>
24
25 #include <sal/log.hxx>
26 #include <i18nlangtag/languagetag.hxx>
27 #include <sfx2/infobar.hxx>
28 #include <sfx2/objsh.hxx>
29 #include <comphelper/processfactory.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 <tools/diagnose_ex.h>
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>
44 #include <config_folders.h>
45
46 #include <officecfg/Office/Common.hxx>
47
48 using namespace com::sun::star;
49
50 namespace
51 {
52
PROP_BACNAME()53 const OUString& PROP_BACNAME()
54 {
55 static const OUString sProp("BusinessAuthorizationCategory:Name");
56 return sProp;
57 }
58
PROP_STARTVALIDITY()59 const OUString& PROP_STARTVALIDITY()
60 {
61 static const OUString sProp("Authorization:StartValidity");
62 return sProp;
63 }
64
PROP_NONE()65 const OUString& PROP_NONE()
66 {
67 static const OUString sProp("None");
68 return sProp;
69 }
70
PROP_IMPACTSCALE()71 const OUString& PROP_IMPACTSCALE()
72 {
73 static const OUString sProp("Impact:Scale");
74 return sProp;
75 }
76
PROP_IMPACTLEVEL()77 const OUString& PROP_IMPACTLEVEL()
78 {
79 static const OUString sProp("Impact:Level:Confidentiality");
80 return sProp;
81 }
82
PROP_PREFIX_EXPORTCONTROL()83 const OUString& PROP_PREFIX_EXPORTCONTROL()
84 {
85 static const OUString sProp("urn:bails:ExportControl:");
86 return sProp;
87 }
88
PROP_PREFIX_NATIONALSECURITY()89 const OUString& PROP_PREFIX_NATIONALSECURITY()
90 {
91 static const OUString sProp("urn:bails:NationalSecurity:");
92 return sProp;
93 }
94
95 /// Represents one category of a classification policy.
96 class SfxClassificationCategory
97 {
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;
105 };
106
107 /// Parses a policy XML conforming to the TSCP BAF schema.
108 class SfxClassificationParser : public cppu::WeakImplHelper<xml::sax::XDocumentHandler>
109 {
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;
115
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;
130
131 /// Pointer to a value in m_aCategories, the currently parsed category.
132 SfxClassificationCategory* m_pCategory = nullptr;
133
134 SfxClassificationParser();
135
136 void SAL_CALL startDocument() override;
137
138 void SAL_CALL endDocument() override;
139
140 void SAL_CALL startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs) override;
141
142 void SAL_CALL endElement(const OUString& rName) override;
143
144 void SAL_CALL characters(const OUString& rChars) override;
145
146 void SAL_CALL ignorableWhitespace(const OUString& rWhitespaces) override;
147
148 void SAL_CALL processingInstruction(const OUString& rTarget, const OUString& rData) override;
149
150 void SAL_CALL setDocumentLocator(const uno::Reference<xml::sax::XLocator>& xLocator) override;
151 };
152
153 SfxClassificationParser::SfxClassificationParser() = default;
154
startDocument()155 void SAL_CALL SfxClassificationParser::startDocument()
156 {
157 }
158
endDocument()159 void SAL_CALL SfxClassificationParser::endDocument()
160 {
161 }
162
startElement(const OUString & rName,const uno::Reference<xml::sax::XAttributeList> & xAttribs)163 void SAL_CALL SfxClassificationParser::startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs)
164 {
165 if (rName == "baf:PolicyAuthorityName")
166 {
167 m_aPolicyAuthorityName.clear();
168 m_bInPolicyAuthorityName = true;
169 }
170 else if (rName == "baf:PolicyName")
171 {
172 m_aPolicyName.clear();
173 m_bInPolicyName = true;
174 }
175 else if (rName == "baf:ProgramID")
176 {
177 m_aProgramID.clear();
178 m_bInProgramID = true;
179 }
180 else if (rName == "baf:BusinessAuthorizationCategory")
181 {
182 const OUString aName = xAttribs->getValueByName("Name");
183 if (!m_pCategory && !aName.isEmpty())
184 {
185 OUString aIdentifier = xAttribs->getValueByName("Identifier");
186
187 // Create a new category and initialize it with the data that's true for all categories.
188 m_aCategories.emplace_back(SfxClassificationCategory());
189 SfxClassificationCategory& rCategory = m_aCategories.back();
190
191 rCategory.m_aName = aName;
192 // Set the abbreviated name, if any, otherwise fallback on the full name.
193 const OUString aAbbreviatedName = xAttribs->getValueByName("loextAbbreviatedName");
194 rCategory.m_aAbbreviatedName = !aAbbreviatedName.isEmpty() ? aAbbreviatedName : aName;
195 rCategory.m_aIdentifier = aIdentifier;
196
197 rCategory.m_aLabels["PolicyAuthority:Name"] = m_aPolicyAuthorityName;
198 rCategory.m_aLabels["Policy:Name"] = m_aPolicyName;
199 rCategory.m_aLabels["BusinessAuthorization:Identifier"] = m_aProgramID;
200 rCategory.m_aLabels["BusinessAuthorizationCategory:Identifier"] = aIdentifier;
201
202 // Also initialize defaults.
203 rCategory.m_aLabels["PolicyAuthority:Identifier"] = PROP_NONE();
204 rCategory.m_aLabels["PolicyAuthority:Country"] = PROP_NONE();
205 rCategory.m_aLabels["Policy:Identifier"] = PROP_NONE();
206 rCategory.m_aLabels["BusinessAuthorization:Name"] = PROP_NONE();
207 rCategory.m_aLabels["BusinessAuthorization:Locator"] = PROP_NONE();
208 rCategory.m_aLabels["BusinessAuthorizationCategory:Identifier:OID"] = PROP_NONE();
209 rCategory.m_aLabels["BusinessAuthorizationCategory:Locator"] = PROP_NONE();
210 rCategory.m_aLabels["BusinessAuthorization:Locator"] = PROP_NONE();
211 rCategory.m_aLabels["MarkingPrecedence"] = PROP_NONE();
212 rCategory.m_aLabels["Marking:general-summary"].clear();
213 rCategory.m_aLabels["Marking:general-warning-statement"].clear();
214 rCategory.m_aLabels["Marking:general-warning-statement:ext:2"].clear();
215 rCategory.m_aLabels["Marking:general-warning-statement:ext:3"].clear();
216 rCategory.m_aLabels["Marking:general-warning-statement:ext:4"].clear();
217 rCategory.m_aLabels["Marking:general-distribution-statement"].clear();
218 rCategory.m_aLabels["Marking:general-distribution-statement:ext:2"].clear();
219 rCategory.m_aLabels["Marking:general-distribution-statement:ext:3"].clear();
220 rCategory.m_aLabels["Marking:general-distribution-statement:ext:4"].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["Marking:email-first-line-of-text"].clear();
225 rCategory.m_aLabels["Marking:email-last-line-of-text"].clear();
226 rCategory.m_aLabels["Marking:email-subject-prefix"].clear();
227 rCategory.m_aLabels["Marking:email-subject-suffix"].clear();
228 rCategory.m_aLabels[PROP_STARTVALIDITY()] = PROP_NONE();
229 rCategory.m_aLabels["Authorization:StopValidity"] = PROP_NONE();
230 m_pCategory = &rCategory;
231 }
232 }
233 else if (rName == "loext:Marking")
234 {
235 OUString aName = xAttribs->getValueByName("Name");
236 m_aMarkings.push_back(aName);
237 }
238 else if (rName == "loext:IntellectualPropertyPart")
239 {
240 OUString aName = xAttribs->getValueByName("Name");
241 m_aIPParts.push_back(aName);
242 }
243 else if (rName == "loext:IntellectualPropertyPartNumber")
244 {
245 OUString aName = xAttribs->getValueByName("Name");
246 m_aIPPartNumbers.push_back(aName);
247 }
248 else if (rName == "baf:Scale")
249 {
250 m_aScale.clear();
251 m_bInScale = true;
252 }
253 else if (rName == "baf:ConfidentalityValue")
254 {
255 m_aConfidentalityValue.clear();
256 m_bInConfidentalityValue = true;
257 }
258 else if (rName == "baf:Identifier")
259 {
260 m_aIdentifier.clear();
261 m_bInIdentifier = true;
262 }
263 else if (rName == "baf:Value")
264 {
265 m_aValue.clear();
266 m_bInValue = true;
267 }
268 }
269
endElement(const OUString & rName)270 void SAL_CALL SfxClassificationParser::endElement(const OUString& rName)
271 {
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")
281 {
282 m_bInScale = false;
283 if (m_pCategory)
284 m_pCategory->m_aLabels[PROP_IMPACTSCALE()] = m_aScale;
285 }
286 else if (rName == "baf:ConfidentalityValue")
287 {
288 m_bInConfidentalityValue = false;
289 if (m_pCategory)
290 {
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 if (rLabels.find("Impact:Level:Integrity") == rLabels.end())
297 rLabels["Impact:Level:Integrity"] = m_aConfidentalityValue;
298 if (rLabels.find("Impact:Level:Availability") == rLabels.end())
299 rLabels["Impact:Level:Availability"] = m_aConfidentalityValue;
300 }
301 }
302 else if (rName == "baf:Identifier")
303 m_bInIdentifier = false;
304 else if (rName == "baf:Value")
305 {
306 if (m_pCategory)
307 {
308 if (m_aIdentifier == "Document: Header")
309 m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCHEADER()] = m_aValue;
310 else if (m_aIdentifier == "Document: Footer")
311 m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCFOOTER()] = m_aValue;
312 else if (m_aIdentifier == "Document: Watermark")
313 m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCWATERMARK()] = m_aValue;
314 }
315 }
316 }
317
characters(const OUString & rChars)318 void SAL_CALL SfxClassificationParser::characters(const OUString& rChars)
319 {
320 if (m_bInPolicyAuthorityName)
321 m_aPolicyAuthorityName += rChars;
322 else if (m_bInPolicyName)
323 m_aPolicyName += rChars;
324 else if (m_bInProgramID)
325 m_aProgramID += rChars;
326 else if (m_bInScale)
327 m_aScale += rChars;
328 else if (m_bInConfidentalityValue)
329 m_aConfidentalityValue += rChars;
330 else if (m_bInIdentifier)
331 m_aIdentifier += rChars;
332 else if (m_bInValue)
333 m_aValue += rChars;
334 }
335
ignorableWhitespace(const OUString &)336 void SAL_CALL SfxClassificationParser::ignorableWhitespace(const OUString& /*rWhitespace*/)
337 {
338 }
339
processingInstruction(const OUString &,const OUString &)340 void SAL_CALL SfxClassificationParser::processingInstruction(const OUString& /*rTarget*/, const OUString& /*rData*/)
341 {
342 }
343
setDocumentLocator(const uno::Reference<xml::sax::XLocator> &)344 void SAL_CALL SfxClassificationParser::setDocumentLocator(const uno::Reference<xml::sax::XLocator>& /*xLocator*/)
345 {
346 }
347
348 } // anonymous namespace
349
350 /// Implementation details of SfxClassificationHelper.
351 class SfxClassificationHelper::Impl
352 {
353 public:
354 /// Selected categories, one category for each policy type.
355 std::map<SfxClassificationPolicyType, SfxClassificationCategory> m_aCategory;
356 /// Possible categories of a policy to choose from.
357 std::vector<SfxClassificationCategory> m_aCategories;
358 std::vector<OUString> m_aMarkings;
359 std::vector<OUString> m_aIPParts;
360 std::vector<OUString> m_aIPPartNumbers;
361
362 uno::Reference<document::XDocumentProperties> m_xDocumentProperties;
363
364 bool const m_bUseLocalized;
365
366 explicit Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized);
367 void parsePolicy();
368 /// Synchronize m_aLabels back to the document properties.
369 void pushToDocumentProperties();
370 /// Set the classification start date to the system time.
371 void setStartValidity(SfxClassificationPolicyType eType);
372 };
373
Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties,bool bUseLocalized)374 SfxClassificationHelper::Impl::Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized)
375 : m_xDocumentProperties(std::move(xDocumentProperties))
376 , m_bUseLocalized(bUseLocalized)
377 {
378 parsePolicy();
379 }
380
parsePolicy()381 void SfxClassificationHelper::Impl::parsePolicy()
382 {
383 uno::Reference<uno::XComponentContext> xComponentContext = comphelper::getProcessComponentContext();
384 SvtPathOptions aOptions;
385 OUString aPath = aOptions.GetClassificationPath();
386
387 // See if there is a localized variant next to the configured XML.
388 OUString aExtension(".xml");
389 if (aPath.endsWith(aExtension) && m_bUseLocalized)
390 {
391 OUString aBase = aPath.copy(0, aPath.getLength() - aExtension.getLength());
392 const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
393 // Expected format is "<original path>_xx-XX.xml".
394 OUString aLocalized = aBase + "_" + rLanguageTag.getBcp47() + aExtension;
395 if (FStatHelper::IsDocument(aLocalized))
396 aPath = aLocalized;
397 }
398
399 std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(aPath, StreamMode::READ);
400 uno::Reference<io::XInputStream> xInputStream(new utl::OStreamWrapper(std::move(pStream)));
401 xml::sax::InputSource aParserInput;
402 aParserInput.aInputStream = xInputStream;
403
404 uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xComponentContext);
405 rtl::Reference<SfxClassificationParser> xClassificationParser(new SfxClassificationParser());
406 uno::Reference<xml::sax::XDocumentHandler> xHandler(xClassificationParser.get());
407 xParser->setDocumentHandler(xHandler);
408 try
409 {
410 xParser->parseStream(aParserInput);
411 }
412 catch (const xml::sax::SAXParseException&)
413 {
414 TOOLS_WARN_EXCEPTION("sfx.view", "parsePolicy() failed");
415 }
416 m_aCategories = xClassificationParser->m_aCategories;
417 m_aMarkings = xClassificationParser->m_aMarkings;
418 m_aIPParts = xClassificationParser->m_aIPParts;
419 m_aIPPartNumbers = xClassificationParser->m_aIPPartNumbers;
420 }
421
lcl_containsProperty(const uno::Sequence<beans::Property> & rProperties,const OUString & rName)422 static bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties, const OUString& rName)
423 {
424 return std::any_of(rProperties.begin(), rProperties.end(), [&](const beans::Property& rProperty)
425 {
426 return rProperty.Name == rName;
427 });
428 }
429
setStartValidity(SfxClassificationPolicyType eType)430 void SfxClassificationHelper::Impl::setStartValidity(SfxClassificationPolicyType eType)
431 {
432 auto itCategory = m_aCategory.find(eType);
433 if (itCategory == m_aCategory.end())
434 return;
435
436 SfxClassificationCategory& rCategory = itCategory->second;
437 auto it = rCategory.m_aLabels.find(policyTypeToString(eType) + PROP_STARTVALIDITY());
438 if (it != rCategory.m_aLabels.end())
439 {
440 if (it->second == PROP_NONE())
441 {
442 // The policy left the start date unchanged, replace it with the system time.
443 util::DateTime aDateTime = DateTime(DateTime::SYSTEM).GetUNODateTime();
444 it->second = utl::toISO8601(aDateTime);
445 }
446 }
447 }
448
pushToDocumentProperties()449 void SfxClassificationHelper::Impl::pushToDocumentProperties()
450 {
451 uno::Reference<beans::XPropertyContainer> xPropertyContainer = m_xDocumentProperties->getUserDefinedProperties();
452 uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
453 uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
454 for (auto& rPair : m_aCategory)
455 {
456 SfxClassificationPolicyType eType = rPair.first;
457 SfxClassificationCategory& rCategory = rPair.second;
458 std::map<OUString, OUString> aLabels = rCategory.m_aLabels;
459 aLabels[policyTypeToString(eType) + PROP_BACNAME()] = rCategory.m_aName;
460 for (const auto& rLabel : aLabels)
461 {
462 try
463 {
464 if (lcl_containsProperty(aProperties, rLabel.first))
465 xPropertySet->setPropertyValue(rLabel.first, uno::makeAny(rLabel.second));
466 else
467 xPropertyContainer->addProperty(rLabel.first, beans::PropertyAttribute::REMOVABLE, uno::makeAny(rLabel.second));
468 }
469 catch (const uno::Exception&)
470 {
471 TOOLS_WARN_EXCEPTION("sfx.view", "pushDocumentProperties() failed for property " << rLabel.first);
472 }
473 }
474 }
475 }
476
IsClassified(const uno::Reference<document::XDocumentProperties> & xDocumentProperties)477 bool SfxClassificationHelper::IsClassified(const uno::Reference<document::XDocumentProperties>& xDocumentProperties)
478 {
479 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
480 if (!xPropertyContainer.is())
481 return false;
482
483 uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
484 const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
485 for (const beans::Property& rProperty : aProperties)
486 {
487 if (rProperty.Name.startsWith("urn:bails:"))
488 return true;
489 }
490
491 return false;
492 }
493
CheckPaste(const uno::Reference<document::XDocumentProperties> & xSource,const uno::Reference<document::XDocumentProperties> & xDestination)494 SfxClassificationCheckPasteResult SfxClassificationHelper::CheckPaste(const uno::Reference<document::XDocumentProperties>& xSource,
495 const uno::Reference<document::XDocumentProperties>& xDestination)
496 {
497 if (!SfxClassificationHelper::IsClassified(xSource))
498 // No classification on the source side. Return early, regardless the
499 // state of the destination side.
500 return SfxClassificationCheckPasteResult::None;
501
502 if (!SfxClassificationHelper::IsClassified(xDestination))
503 {
504 // Paste from a classified document to a non-classified one -> deny.
505 return SfxClassificationCheckPasteResult::TargetDocNotClassified;
506 }
507
508 // Remaining case: paste between two classified documents.
509 SfxClassificationHelper aSource(xSource);
510 SfxClassificationHelper aDestination(xDestination);
511 if (aSource.GetImpactScale() != aDestination.GetImpactScale())
512 // It's possible to compare them if they have the same scale.
513 return SfxClassificationCheckPasteResult::None;
514
515 if (aSource.GetImpactLevel() > aDestination.GetImpactLevel())
516 // Paste from a doc that has higher classification -> deny.
517 return SfxClassificationCheckPasteResult::DocClassificationTooLow;
518
519 return SfxClassificationCheckPasteResult::None;
520 }
521
ShowPasteInfo(SfxClassificationCheckPasteResult eResult)522 bool SfxClassificationHelper::ShowPasteInfo(SfxClassificationCheckPasteResult eResult)
523 {
524 switch (eResult)
525 {
526 case SfxClassificationCheckPasteResult::None:
527 {
528 return true;
529 }
530 break;
531 case SfxClassificationCheckPasteResult::TargetDocNotClassified:
532 {
533 if (!Application::IsHeadlessModeEnabled())
534 {
535 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
536 VclMessageType::Info, VclButtonsType::Ok,
537 SfxResId(STR_TARGET_DOC_NOT_CLASSIFIED)));
538 xBox->run();
539 }
540 return false;
541 }
542 break;
543 case SfxClassificationCheckPasteResult::DocClassificationTooLow:
544 {
545 if (!Application::IsHeadlessModeEnabled())
546 {
547 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
548 VclMessageType::Info, VclButtonsType::Ok,
549 SfxResId(STR_DOC_CLASSIFICATION_TOO_LOW)));
550 xBox->run();
551 }
552 return false;
553 }
554 break;
555 }
556
557 return true;
558 }
559
SfxClassificationHelper(const uno::Reference<document::XDocumentProperties> & xDocumentProperties,bool bUseLocalizedPolicy)560 SfxClassificationHelper::SfxClassificationHelper(const uno::Reference<document::XDocumentProperties>& xDocumentProperties, bool bUseLocalizedPolicy)
561 : m_pImpl(std::make_unique<Impl>(xDocumentProperties, bUseLocalizedPolicy))
562 {
563 if (!xDocumentProperties.is())
564 return;
565
566 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
567 if (!xPropertyContainer.is())
568 return;
569
570 uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
571 const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
572 for (const beans::Property& rProperty : aProperties)
573 {
574 if (!rProperty.Name.startsWith("urn:bails:"))
575 continue;
576
577 uno::Any aAny = xPropertySet->getPropertyValue(rProperty.Name);
578 OUString aValue;
579 if (aAny >>= aValue)
580 {
581 SfxClassificationPolicyType eType = stringToPolicyType(rProperty.Name);
582 OUString aPrefix = policyTypeToString(eType);
583 if (!rProperty.Name.startsWith(aPrefix))
584 // It's a prefix we did not recognize, ignore.
585 continue;
586
587 //TODO: Support abbreviated names(?)
588 if (rProperty.Name == (aPrefix + PROP_BACNAME()))
589 m_pImpl->m_aCategory[eType].m_aName = aValue;
590 else
591 m_pImpl->m_aCategory[eType].m_aLabels[rProperty.Name] = aValue;
592 }
593 }
594 }
595
596 SfxClassificationHelper::~SfxClassificationHelper() = default;
597
GetMarkings() const598 std::vector<OUString> const & SfxClassificationHelper::GetMarkings() const
599 {
600 return m_pImpl->m_aMarkings;
601 }
602
GetIntellectualPropertyParts() const603 std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyParts() const
604 {
605 return m_pImpl->m_aIPParts;
606 }
607
GetIntellectualPropertyPartNumbers() const608 std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyPartNumbers() const
609 {
610 return m_pImpl->m_aIPPartNumbers;
611 }
612
GetBACName(SfxClassificationPolicyType eType) const613 const OUString& SfxClassificationHelper::GetBACName(SfxClassificationPolicyType eType) const
614 {
615 return m_pImpl->m_aCategory[eType].m_aName;
616 }
617
GetAbbreviatedBACName(const OUString & sFullName)618 const OUString& SfxClassificationHelper::GetAbbreviatedBACName(const OUString& sFullName)
619 {
620 for (const auto& category : m_pImpl->m_aCategories)
621 {
622 if (category.m_aName == sFullName)
623 return category.m_aAbbreviatedName;
624 }
625
626 return sFullName;
627 }
628
GetBACNameForIdentifier(const OUString & sIdentifier)629 OUString SfxClassificationHelper::GetBACNameForIdentifier(const OUString& sIdentifier)
630 {
631 OUString aRet;
632 if (sIdentifier.isEmpty())
633 return aRet;
634
635 for (const auto& category : m_pImpl->m_aCategories)
636 {
637 if (category.m_aIdentifier == sIdentifier)
638 return category.m_aName;
639 }
640
641 return aRet;
642 }
643
GetHigherClass(const OUString & first,const OUString & second)644 OUString SfxClassificationHelper::GetHigherClass(const OUString& first, const OUString& second)
645 {
646 size_t nFirstConfidentiality = 0;
647 size_t nSecondConfidentiality = 0;
648 for (const auto& category : m_pImpl->m_aCategories)
649 {
650 if (category.m_aName == first)
651 nFirstConfidentiality = category.m_nConfidentiality;
652 if (category.m_aName == second)
653 nSecondConfidentiality = category.m_nConfidentiality;
654 }
655
656 return nFirstConfidentiality >= nSecondConfidentiality ? first : second;
657 }
658
HasImpactLevel()659 bool SfxClassificationHelper::HasImpactLevel()
660 {
661 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
662 if (itCategory == m_pImpl->m_aCategory.end())
663 return false;
664
665 SfxClassificationCategory& rCategory = itCategory->second;
666 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
667 if (it == rCategory.m_aLabels.end())
668 return false;
669
670 it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
671 return it != rCategory.m_aLabels.end();
672 }
673
HasDocumentHeader()674 bool SfxClassificationHelper::HasDocumentHeader()
675 {
676 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
677 if (itCategory == m_pImpl->m_aCategory.end())
678 return false;
679
680 SfxClassificationCategory& rCategory = itCategory->second;
681 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCHEADER());
682 return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
683 }
684
HasDocumentFooter()685 bool SfxClassificationHelper::HasDocumentFooter()
686 {
687 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
688 if (itCategory == m_pImpl->m_aCategory.end())
689 return false;
690
691 SfxClassificationCategory& rCategory = itCategory->second;
692 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCFOOTER());
693 return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
694 }
695
GetImpactLevelType()696 InfobarType SfxClassificationHelper::GetImpactLevelType()
697 {
698 InfobarType aRet;
699
700 aRet = InfobarType::WARNING;
701
702 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
703 if (itCategory == m_pImpl->m_aCategory.end())
704 return aRet;
705
706 SfxClassificationCategory& rCategory = itCategory->second;
707 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
708 OUString aScale = it->second;
709 if (it == rCategory.m_aLabels.end())
710 return aRet;
711
712 it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
713 OUString aLevel = it->second;
714 if (it == rCategory.m_aLabels.end())
715 return aRet;
716
717 // The spec defines two valid scale values: FIPS-199 and UK-Cabinet.
718 if (aScale == "UK-Cabinet")
719 {
720 if (aLevel == "0")
721 aRet = InfobarType::SUCCESS;
722 else if (aLevel == "1")
723 aRet = InfobarType::WARNING;
724 else if (aLevel == "2")
725 aRet = InfobarType::WARNING;
726 else if (aLevel == "3")
727 aRet = InfobarType::DANGER;
728 }
729 else if (aScale == "FIPS-199")
730 {
731 if (aLevel == "Low")
732 aRet = InfobarType::SUCCESS;
733 else if (aLevel == "Moderate")
734 aRet = InfobarType::WARNING;
735 else if (aLevel == "High")
736 aRet = InfobarType::DANGER;
737 }
738 return aRet;
739 }
740
GetImpactLevel()741 sal_Int32 SfxClassificationHelper::GetImpactLevel()
742 {
743 sal_Int32 nRet = -1;
744
745 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
746 if (itCategory == m_pImpl->m_aCategory.end())
747 return nRet;
748
749 SfxClassificationCategory& rCategory = itCategory->second;
750 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
751 if (it == rCategory.m_aLabels.end())
752 return nRet;
753 OUString aScale = it->second;
754
755 it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
756 if (it == rCategory.m_aLabels.end())
757 return nRet;
758 OUString aLevel = it->second;
759
760 if (aScale == "UK-Cabinet")
761 {
762 sal_Int32 nValue = aLevel.toInt32();
763 if (nValue < 0 || nValue > 3)
764 return nRet;
765 nRet = nValue;
766 }
767 else if (aScale == "FIPS-199")
768 {
769 static std::map<OUString, sal_Int32> const aValues
770 {
771 { "Low", 0 },
772 { "Moderate", 1 },
773 { "High", 2 }
774 };
775 auto itValues = aValues.find(aLevel);
776 if (itValues == aValues.end())
777 return nRet;
778 nRet = itValues->second;
779 }
780
781 return nRet;
782 }
783
GetImpactScale()784 OUString SfxClassificationHelper::GetImpactScale()
785 {
786 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
787 if (itCategory == m_pImpl->m_aCategory.end())
788 return OUString();
789
790 SfxClassificationCategory& rCategory = itCategory->second;
791 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
792 if (it != rCategory.m_aLabels.end())
793 return it->second;
794
795 return OUString();
796 }
797
GetDocumentWatermark()798 OUString SfxClassificationHelper::GetDocumentWatermark()
799 {
800 auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
801 if (itCategory == m_pImpl->m_aCategory.end())
802 return OUString();
803
804 SfxClassificationCategory& rCategory = itCategory->second;
805 auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCWATERMARK());
806 if (it != rCategory.m_aLabels.end())
807 return it->second;
808
809 return OUString();
810 }
811
GetBACNames()812 std::vector<OUString> SfxClassificationHelper::GetBACNames()
813 {
814 if (m_pImpl->m_aCategories.empty())
815 m_pImpl->parsePolicy();
816
817 std::vector<OUString> aRet;
818 std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
819 {
820 return rCategory.m_aName;
821 });
822 return aRet;
823 }
824
GetBACIdentifiers()825 std::vector<OUString> SfxClassificationHelper::GetBACIdentifiers()
826 {
827 if (m_pImpl->m_aCategories.empty())
828 m_pImpl->parsePolicy();
829
830 std::vector<OUString> aRet;
831 std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
832 {
833 return rCategory.m_aIdentifier;
834 });
835 return aRet;
836 }
837
GetAbbreviatedBACNames()838 std::vector<OUString> SfxClassificationHelper::GetAbbreviatedBACNames()
839 {
840 if (m_pImpl->m_aCategories.empty())
841 m_pImpl->parsePolicy();
842
843 std::vector<OUString> aRet;
844 std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
845 {
846 return rCategory.m_aAbbreviatedName;
847 });
848 return aRet;
849 }
850
SetBACName(const OUString & rName,SfxClassificationPolicyType eType)851 void SfxClassificationHelper::SetBACName(const OUString& rName, SfxClassificationPolicyType eType)
852 {
853 if (m_pImpl->m_aCategories.empty())
854 m_pImpl->parsePolicy();
855
856 auto it = std::find_if(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), [&](const SfxClassificationCategory& rCategory)
857 {
858 return rCategory.m_aName == rName;
859 });
860 if (it == m_pImpl->m_aCategories.end())
861 {
862 SAL_WARN("sfx.view", "'" << rName << "' is not a recognized category name");
863 return;
864 }
865
866 m_pImpl->m_aCategory[eType].m_aName = it->m_aName;
867 m_pImpl->m_aCategory[eType].m_aAbbreviatedName = it->m_aAbbreviatedName;
868 m_pImpl->m_aCategory[eType].m_nConfidentiality = it->m_nConfidentiality;
869 m_pImpl->m_aCategory[eType].m_aLabels.clear();
870 const OUString& rPrefix = policyTypeToString(eType);
871 for (const auto& rLabel : it->m_aLabels)
872 m_pImpl->m_aCategory[eType].m_aLabels[rPrefix + rLabel.first] = rLabel.second;
873
874 m_pImpl->setStartValidity(eType);
875 m_pImpl->pushToDocumentProperties();
876 SfxViewFrame* pViewFrame = SfxViewFrame::Current();
877 if (!pViewFrame)
878 return;
879
880 UpdateInfobar(*pViewFrame);
881 }
882
UpdateInfobar(SfxViewFrame & rViewFrame)883 void SfxClassificationHelper::UpdateInfobar(SfxViewFrame& rViewFrame)
884 {
885 OUString aBACName = GetBACName(SfxClassificationPolicyType::IntellectualProperty);
886 bool bImpactLevel = HasImpactLevel();
887 if (!aBACName.isEmpty() && bImpactLevel)
888 {
889 OUString aMessage = SfxResId(STR_CLASSIFIED_DOCUMENT);
890 aMessage = aMessage.replaceFirst("%1", aBACName);
891
892 rViewFrame.RemoveInfoBar("classification");
893 rViewFrame.AppendInfoBar("classification", "", aMessage, GetImpactLevelType());
894 }
895 }
896
stringToPolicyType(const OUString & rType)897 SfxClassificationPolicyType SfxClassificationHelper::stringToPolicyType(const OUString& rType)
898 {
899 if (rType.startsWith(PROP_PREFIX_EXPORTCONTROL()))
900 return SfxClassificationPolicyType::ExportControl;
901 else if (rType.startsWith(PROP_PREFIX_NATIONALSECURITY()))
902 return SfxClassificationPolicyType::NationalSecurity;
903 else
904 return SfxClassificationPolicyType::IntellectualProperty;
905 }
906
policyTypeToString(SfxClassificationPolicyType eType)907 const OUString& SfxClassificationHelper::policyTypeToString(SfxClassificationPolicyType eType)
908 {
909 switch (eType)
910 {
911 case SfxClassificationPolicyType::ExportControl:
912 return PROP_PREFIX_EXPORTCONTROL();
913 break;
914 case SfxClassificationPolicyType::NationalSecurity:
915 return PROP_PREFIX_NATIONALSECURITY();
916 break;
917 case SfxClassificationPolicyType::IntellectualProperty:
918 break;
919 }
920
921 return PROP_PREFIX_INTELLECTUALPROPERTY();
922 }
923
PROP_DOCHEADER()924 const OUString& SfxClassificationHelper::PROP_DOCHEADER()
925 {
926 static const OUString sProp("Marking:document-header");
927 return sProp;
928 }
929
PROP_DOCFOOTER()930 const OUString& SfxClassificationHelper::PROP_DOCFOOTER()
931 {
932 static const OUString sProp("Marking:document-footer");
933 return sProp;
934 }
935
PROP_DOCWATERMARK()936 const OUString& SfxClassificationHelper::PROP_DOCWATERMARK()
937 {
938 static const OUString sProp("Marking:document-watermark");
939 return sProp;
940 }
941
PROP_PREFIX_INTELLECTUALPROPERTY()942 const OUString& SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY()
943 {
944 static const OUString sProp("urn:bails:IntellectualProperty:");
945 return sProp;
946 }
947
getPolicyType()948 SfxClassificationPolicyType SfxClassificationHelper::getPolicyType()
949 {
950 sal_Int32 nPolicyTypeNumber = officecfg::Office::Common::Classification::Policy::get();
951 auto eType = static_cast<SfxClassificationPolicyType>(nPolicyTypeNumber);
952 return eType;
953 }
954
955 namespace sfx
956 {
957
958 namespace
959 {
960
getProperty(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer,OUString const & rName)961 OUString getProperty(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer,
962 OUString const& rName)
963 {
964 try
965 {
966 uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
967 return xPropertySet->getPropertyValue(rName).get<OUString>();
968 }
969 catch (const css::uno::Exception&)
970 {
971 }
972
973 return OUString();
974 }
975
976 } // end anonymous namespace
977
getCreationOriginProperty(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer,sfx::ClassificationKeyCreator const & rKeyCreator)978 sfx::ClassificationCreationOrigin getCreationOriginProperty(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer,
979 sfx::ClassificationKeyCreator const & rKeyCreator)
980 {
981 OUString sValue = getProperty(rxPropertyContainer, rKeyCreator.makeCreationOriginKey());
982 if (sValue.isEmpty())
983 return sfx::ClassificationCreationOrigin::NONE;
984
985 return (sValue == "BAF_POLICY")
986 ? sfx::ClassificationCreationOrigin::BAF_POLICY
987 : sfx::ClassificationCreationOrigin::MANUAL;
988 }
989
990 }
991
992 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
993