LibreOffice Module sfx2 (master) 1
securitypage.cxx
Go to the documentation of this file.
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 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sfx2/htmlmode.hxx>
21
22#include <sfx2/sfxresid.hxx>
23
24#include <sfx2/sfxsids.hrc>
25#include <sfx2/objsh.hxx>
26#include <sfx2/viewsh.hxx>
27#include <sfx2/dispatch.hxx>
28#include <sfx2/passwd.hxx>
29
30#include <vcl/svapp.hxx>
31#include <vcl/weld.hxx>
32#include <svl/eitem.hxx>
33#include <svl/poolitem.hxx>
34#include <svl/intitem.hxx>
37
38#include <sfx2/strings.hrc>
39
40#include "securitypage.hxx"
41
42using namespace ::com::sun::star;
43
44namespace
45{
46 enum RedliningMode { RL_NONE, RL_WRITER, RL_CALC };
47
48 bool QueryState( TypedWhichId<SfxBoolItem> _nSlot, bool& _rValue )
49 {
50 bool bRet = false;
52 if (pViewSh)
53 {
54 const SfxBoolItem* pItem;
55 SfxDispatcher* pDisp = pViewSh->GetDispatcher();
56 SfxItemState nState = pDisp->QueryState( _nSlot, pItem );
57 bRet = SfxItemState::DEFAULT <= nState;
58 if (bRet)
59 _rValue = pItem->GetValue();
60 }
61 return bRet;
62 }
63
64
65 bool QueryRecordChangesProtectionState( RedliningMode _eMode, bool& _rValue )
66 {
67 bool bRet = false;
68 if (_eMode != RL_NONE)
69 {
70 TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_PROTECT : SID_CHG_PROTECT;
71 bRet = QueryState( nSlot, _rValue );
72 }
73 return bRet;
74 }
75
76
77 bool QueryRecordChangesState( RedliningMode _eMode, bool& _rValue )
78 {
79 bool bRet = false;
80 if (_eMode != RL_NONE)
81 {
82 TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_ON : FID_CHG_RECORD;
83 bRet = QueryState( nSlot, _rValue );
84 }
85 return bRet;
86 }
87}
88
89
90static bool lcl_GetPassword(
91 weld::Window *pParent,
92 bool bProtect,
93 /*out*/OUString &rPassword )
94{
95 bool bRes = false;
96 SfxPasswordDialog aPasswdDlg(pParent);
97 aPasswdDlg.SetMinLen(1);
98 if (bProtect)
100 if (RET_OK == aPasswdDlg.run() && !aPasswdDlg.GetPassword().isEmpty())
101 {
102 rPassword = aPasswdDlg.GetPassword();
103 bRes = true;
104 }
105 return bRes;
106}
107
108
109static bool lcl_IsPasswordCorrect(weld::Window *pParent, std::u16string_view rPassword)
110{
111 SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
112 if (!pCurDocShell)
113 return false;
114
115 bool bRes = false;
116 uno::Sequence< sal_Int8 > aPasswordHash;
117 pCurDocShell->GetProtectionHash( aPasswordHash );
118
119 // check if supplied password was correct
120 if (aPasswordHash.getLength() == 1 && aPasswordHash[0] == 1)
121 {
122 // dummy RedlinePassword from OOXML import: get real password info
123 // from the grab-bag to verify the password
124 const css::uno::Sequence< css::beans::PropertyValue > aDocumentProtection =
125 pCurDocShell->GetDocumentProtectionFromGrabBag();
126 bRes =
127 // password is ok, if there is no DocumentProtection in the GrabBag,
128 // i.e. the dummy RedlinePassword imported from an OpenDocument file
129 !aDocumentProtection.hasElements() ||
130 // verify password with the password info imported from OOXML
133 }
134 else
135 {
136 uno::Sequence< sal_Int8 > aNewPasswd( aPasswordHash );
137 SvPasswordHelper::GetHashPassword( aNewPasswd, rPassword );
138 bRes = SvPasswordHelper::CompareHashPassword( aPasswordHash, rPassword );
139 }
140
141 if ( !bRes )
142 {
143 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
144 VclMessageType::Info, VclButtonsType::Ok,
145 SfxResId(RID_SVXSTR_INCORRECT_PASSWORD)));
146 xInfoBox->run();
147 }
148
149 return bRes;
150}
151
153{
155
156 RedliningMode m_eRedlingMode; // for record changes
157
161
164
165 std::unique_ptr<weld::CheckButton> m_xOpenReadonlyCB;
166 std::unique_ptr<weld::CheckButton> m_xRecordChangesCB; // for record changes
167 std::unique_ptr<weld::Button> m_xProtectPB; // for record changes
168 std::unique_ptr<weld::Button> m_xUnProtectPB; // for record changes
169
170 DECL_LINK(RecordChangesCBToggleHdl, weld::Toggleable&, void);
171 DECL_LINK(ChangeProtectionPBHdl, weld::Button&, void);
172
174
175 bool FillItemSet_Impl();
176 void Reset_Impl();
177};
178
180 : m_rMyTabPage(rTabPage)
181 , m_eRedlingMode(RL_NONE)
182 , m_bOrigPasswordIsConfirmed(false)
183 , m_bNewPasswordIsValid(false)
184 , m_aEndRedliningWarning(SfxResId(RID_SVXSTR_END_REDLINING_WARNING))
185 , m_bEndRedliningWarningDone(false)
186 , m_xOpenReadonlyCB(rTabPage.GetBuilder().weld_check_button("readonly"))
187 , m_xRecordChangesCB(rTabPage.GetBuilder().weld_check_button("recordchanges"))
188 , m_xProtectPB(rTabPage.GetBuilder().weld_button("protect"))
189 , m_xUnProtectPB(rTabPage.GetBuilder().weld_button("unprotect"))
190{
191 m_xProtectPB->show();
192 m_xUnProtectPB->hide();
193
194 m_xRecordChangesCB->connect_toggled(LINK(this, SfxSecurityPage_Impl, RecordChangesCBToggleHdl));
195 m_xProtectPB->connect_clicked(LINK(this, SfxSecurityPage_Impl, ChangeProtectionPBHdl));
196 m_xUnProtectPB->connect_clicked(LINK(this, SfxSecurityPage_Impl, ChangeProtectionPBHdl));
197}
198
200{
201 bool bModified = false;
202
203 SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
204 if (pCurDocShell && !pCurDocShell->IsReadOnly())
205 {
206 if (m_eRedlingMode != RL_NONE )
207 {
208 const bool bDoRecordChanges = m_xRecordChangesCB->get_active();
209 const bool bDoChangeProtection = m_xUnProtectPB->get_visible();
210
211 // sanity checks
212 DBG_ASSERT( bDoRecordChanges || !bDoChangeProtection, "no change recording should imply no change protection" );
213 DBG_ASSERT( bDoChangeProtection || !bDoRecordChanges, "no change protection should imply no change recording" );
214 DBG_ASSERT( !bDoChangeProtection || !m_aNewPassword.isEmpty(), "change protection should imply password length is > 0" );
215 DBG_ASSERT( bDoChangeProtection || m_aNewPassword.isEmpty(), "no change protection should imply password length is 0" );
216
217 // change recording
218 if (bDoRecordChanges != pCurDocShell->IsChangeRecording())
219 {
220 pCurDocShell->SetChangeRecording( bDoRecordChanges );
221 bModified = true;
222 }
223
224 // change record protection
226 bDoChangeProtection != pCurDocShell->HasChangeRecordProtection())
227 {
228 DBG_ASSERT( !bDoChangeProtection || bDoRecordChanges,
229 "change protection requires record changes to be active!" );
230 pCurDocShell->SetProtectionPassword( m_aNewPassword );
231 bModified = true;
232 }
233 }
234
235 // open read-only?
236 const bool bDoOpenReadonly = m_xOpenReadonlyCB->get_active();
237 if (bDoOpenReadonly != pCurDocShell->IsSecurityOptOpenReadOnly())
238 {
239 pCurDocShell->SetSecurityOptOpenReadOnly( bDoOpenReadonly );
240 bModified = true;
241 }
242 }
243
244 return bModified;
245}
246
247
249{
250 SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
251
252 if (!pCurDocShell)
253 {
254 // no doc -> hide document settings
255 m_xOpenReadonlyCB->set_sensitive(false);
256 m_xRecordChangesCB->set_sensitive(false);
257 m_xProtectPB->show();
258 m_xProtectPB->set_sensitive(false);
259 m_xUnProtectPB->hide();
260 m_xUnProtectPB->set_sensitive(false);
261 }
262 else
263 {
264 bool bIsHTMLDoc = false;
265 bool bProtect = true, bUnProtect = false;
267 if (pViewSh)
268 {
269 const SfxUInt16Item* pItem;
270 SfxDispatcher* pDisp = pViewSh->GetDispatcher();
271 if (SfxItemState::DEFAULT <= pDisp->QueryState( SID_HTML_MODE, pItem ))
272 {
273 sal_uInt16 nMode = pItem->GetValue();
274 bIsHTMLDoc = ( ( nMode & HTMLMODE_ON ) != 0 );
275 }
276 }
277
278 bool bIsReadonly = pCurDocShell->IsReadOnly();
279 if (!bIsHTMLDoc)
280 {
281 m_xOpenReadonlyCB->set_active(pCurDocShell->IsSecurityOptOpenReadOnly());
282 m_xOpenReadonlyCB->set_sensitive(!bIsReadonly);
283 }
284 else
285 m_xOpenReadonlyCB->set_sensitive(false);
286
287 bool bRecordChanges;
288 if (QueryRecordChangesState( RL_WRITER, bRecordChanges ) && !bIsHTMLDoc)
289 m_eRedlingMode = RL_WRITER;
290 else if (QueryRecordChangesState( RL_CALC, bRecordChanges ))
291 m_eRedlingMode = RL_CALC;
292 else
293 m_eRedlingMode = RL_NONE;
294
295 if (m_eRedlingMode != RL_NONE)
296 {
297 bool bProtection(false);
298 QueryRecordChangesProtectionState( m_eRedlingMode, bProtection );
299
300 m_xProtectPB->set_sensitive(!bIsReadonly);
301 m_xUnProtectPB->set_sensitive(!bIsReadonly);
302 // set the right text
303 if (bProtection)
304 {
305 bProtect = false;
306 bUnProtect = true;
307 }
308
309 m_xRecordChangesCB->set_active(bRecordChanges);
310 m_xRecordChangesCB->set_sensitive(!bIsReadonly);
311
312 m_bOrigPasswordIsConfirmed = true; // default case if no password is set
313 uno::Sequence< sal_Int8 > aPasswordHash;
314 // check if password is available
315 if (pCurDocShell->GetProtectionHash( aPasswordHash ) &&
316 aPasswordHash.hasElements())
317 m_bOrigPasswordIsConfirmed = false; // password found, needs to be confirmed later on
318 }
319 else
320 {
321 // A Calc document that is shared will have 'm_eRedlingMode == RL_NONE'
322 // In shared documents change recording and protection must be disabled,
323 // similar to documents that do not support change recording at all.
324 m_xRecordChangesCB->set_active(false);
325 m_xRecordChangesCB->set_sensitive(false);
326 m_xProtectPB->set_sensitive(false);
327 m_xUnProtectPB->set_sensitive(false);
328 }
329
330 m_xProtectPB->set_visible(bProtect);
331 m_xUnProtectPB->set_visible(bUnProtect);
332 }
333}
334
335IMPL_LINK_NOARG(SfxSecurityPage_Impl, RecordChangesCBToggleHdl, weld::Toggleable&, void)
336{
337 // when change recording gets disabled protection must be disabled as well
338 if (m_xRecordChangesCB->get_active()) // the new check state is already present, thus the '!'
339 return;
340
341 bool bAlreadyDone = false;
342 if (!m_bEndRedliningWarningDone)
343 {
344 std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(m_rMyTabPage.GetFrameWeld(),
345 VclMessageType::Warning, VclButtonsType::YesNo,
346 m_aEndRedliningWarning));
347 xWarn->set_default_response(RET_NO);
348 if (xWarn->run() != RET_YES)
349 bAlreadyDone = true;
350 else
351 m_bEndRedliningWarningDone = true;
352 }
353
354 const bool bNeedPassword = !m_bOrigPasswordIsConfirmed
355 && m_xUnProtectPB->get_visible(); // tdf#128230 Require password if the Unprotect button is visible
356 if (!bAlreadyDone && bNeedPassword)
357 {
358 OUString aPasswordText;
359
360 // dialog canceled or no password provided
361 if (!lcl_GetPassword( m_rMyTabPage.GetFrameWeld(), false, aPasswordText ))
362 bAlreadyDone = true;
363
364 // ask for password and if dialog is canceled or no password provided return
365 if (lcl_IsPasswordCorrect(m_rMyTabPage.GetFrameWeld(), aPasswordText))
366 m_bOrigPasswordIsConfirmed = true;
367 else
368 bAlreadyDone = true;
369 }
370
371 if (bAlreadyDone)
372 m_xRecordChangesCB->set_active(true); // restore original state
373 else
374 {
375 // remember required values to change protection and change recording in
376 // FillItemSet_Impl later on if password was correct.
377 m_bNewPasswordIsValid = true;
378 m_aNewPassword.clear();
379 m_xProtectPB->show();
380 m_xUnProtectPB->hide();
381 }
382}
383
384IMPL_LINK_NOARG(SfxSecurityPage_Impl, ChangeProtectionPBHdl, weld::Button&, void)
385{
386 if (m_eRedlingMode == RL_NONE)
387 return;
388
389 // the push button text is always the opposite of the current state. Thus:
390 const bool bCurrentProtection = m_xUnProtectPB->get_visible();
391
392 // ask user for password (if still necessary)
393 OUString aPasswordText;
394 bool bNewProtection = !bCurrentProtection;
395 const bool bNeedPassword = bNewProtection || !m_bOrigPasswordIsConfirmed;
396 if (bNeedPassword)
397 {
398 // ask for password and if dialog is canceled or no password provided return
399 if (!lcl_GetPassword(m_rMyTabPage.GetFrameWeld(), bNewProtection, aPasswordText))
400 return;
401
402 // provided password still needs to be checked?
403 if (!bNewProtection && !m_bOrigPasswordIsConfirmed)
404 {
405 if (lcl_IsPasswordCorrect(m_rMyTabPage.GetFrameWeld(), aPasswordText))
406 m_bOrigPasswordIsConfirmed = true;
407 else
408 return;
409 }
410 }
411 DBG_ASSERT( m_bOrigPasswordIsConfirmed, "ooops... this should not have happened!" );
412
413 // remember required values to change protection and change recording in
414 // FillItemSet_Impl later on if password was correct.
415 m_bNewPasswordIsValid = true;
416 m_aNewPassword = bNewProtection? aPasswordText : OUString();
417
418 m_xRecordChangesCB->set_active(bNewProtection);
419
420 m_xUnProtectPB->set_visible(bNewProtection);
421 m_xProtectPB->set_visible(!bNewProtection);
422}
423
424std::unique_ptr<SfxTabPage> SfxSecurityPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet * rItemSet)
425{
426 return std::make_unique<SfxSecurityPage>(pPage, pController, *rItemSet);
427}
428
430 : SfxTabPage(pPage, pController, "sfx/ui/securityinfopage.ui", "SecurityInfoPage", &rItemSet)
431{
432 m_pImpl.reset(new SfxSecurityPage_Impl( *this ));
433}
434
436{
437 bool bModified = false;
438 DBG_ASSERT(m_pImpl, "implementation pointer is 0. Still in c-tor?");
439 if (m_pImpl != nullptr)
440 bModified = m_pImpl->FillItemSet_Impl();
441 return bModified;
442}
443
444void SfxSecurityPage::Reset( const SfxItemSet * /*rItemSet*/ )
445{
446 DBG_ASSERT(m_pImpl, "implementation pointer is 0. Still in c-tor?");
447 if (m_pImpl != nullptr)
448 m_pImpl->Reset_Impl();
449}
450
451/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static weld::MessageDialog * CreateMessageDialog(weld::Widget *pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage, const ILibreOfficeKitNotifier *pNotifier=nullptr)
sal_uInt16 GetValue() const
bool GetValue() const
SfxItemState QueryState(sal_uInt16 nSID, const SfxPoolItem *&rpState)
Definition: dispatch.cxx:1970
virtual bool HasChangeRecordProtection() const
Definition: objxtor.cxx:1097
virtual bool GetProtectionHash(css::uno::Sequence< sal_Int8 > &rPasswordHash)
Definition: objxtor.cxx:1119
bool IsReadOnly() const
Definition: objmisc.cxx:416
virtual bool IsChangeRecording() const
Definition: objxtor.cxx:1089
bool IsSecurityOptOpenReadOnly() const
Definition: objstor.cxx:3158
virtual void SetProtectionPassword(const OUString &rPassword)
Definition: objxtor.cxx:1112
virtual void SetChangeRecording(bool bActivate, bool bLockAllViews=false)
Definition: objxtor.cxx:1105
static SAL_WARN_UNUSED_RESULT SfxObjectShell * Current()
Definition: objxtor.cxx:481
css::uno::Sequence< css::beans::PropertyValue > GetDocumentProtectionFromGrabBag() const
Gets grab-bagged password info to unprotect change tracking with verification.
Definition: objserv.cxx:2236
void SetSecurityOptOpenReadOnly(bool bOpenReadOnly)
Definition: objstor.cxx:3163
OUString GetPassword() const
Definition: passwd.hxx:91
virtual short run() override
Definition: passwd.cxx:213
void SetMinLen(sal_uInt16 Len)
Definition: passwd.cxx:156
void ShowExtras(SfxShowExtras nExtras)
Definition: passwd.hxx:119
virtual bool FillItemSet(SfxItemSet *) override
SfxSecurityPage(weld::Container *pPage, weld::DialogController *pController, const SfxItemSet &)
virtual void Reset(const SfxItemSet *) override
static std::unique_ptr< SfxTabPage > Create(weld::Container *pPage, weld::DialogController *pController, const SfxItemSet *)
std::unique_ptr< SfxSecurityPage_Impl > m_pImpl
SfxDispatcher * GetDispatcher() const
This method returns a pointer to the <SfxDispatcher>, when the SfxShell is currently <UI-active> or a...
Definition: shell.cxx:124
One SfxViewShell more or less represents one edit window for a document, there can be multiple ones f...
Definition: viewsh.hxx:165
static SAL_WARN_UNUSED_RESULT SfxViewShell * Current()
Definition: viewsh.cxx:1848
static SVL_DLLPUBLIC void GetHashPassword(css::uno::Sequence< sal_Int8 > &rPassHash, const char *pPass, sal_uInt32 nLen)
static SVL_DLLPUBLIC bool CompareHashPassword(const css::uno::Sequence< sal_Int8 > &rOldPassHash, std::u16string_view sNewPass)
static bool IsModifyPasswordCorrect(std::u16string_view aPassword, const css::uno::Sequence< css::beans::PropertyValue > &aInfo)
static css::uno::Sequence< css::beans::PropertyValue > ConvertPasswordInfo(const css::uno::Sequence< css::beans::PropertyValue > &aInfo)
#define DBG_ASSERT(sCon, aError)
sal_Int32 nState
@ HTMLMODE_ON
Definition: htmlmode.hxx:24
SfxItemState
static bool lcl_GetPassword(weld::Window *pParent, bool bProtect, OUString &rPassword)
static bool lcl_IsPasswordCorrect(weld::Window *pParent, std::u16string_view rPassword)
IMPL_LINK_NOARG(SfxSecurityPage_Impl, RecordChangesCBToggleHdl, weld::Toggleable &, void)
OUString SfxResId(TranslateId aId)
Definition: sfxresid.cxx:22
DECL_LINK(RecordChangesCBToggleHdl, weld::Toggleable &, void)
std::unique_ptr< weld::CheckButton > m_xOpenReadonlyCB
DECL_LINK(ChangeProtectionPBHdl, weld::Button &, void)
SfxSecurityPage & m_rMyTabPage
std::unique_ptr< weld::CheckButton > m_xRecordChangesCB
SfxSecurityPage_Impl(SfxSecurityPage &rDlg)
std::unique_ptr< weld::Button > m_xProtectPB
OUString m_aEndRedliningWarning
RedliningMode m_eRedlingMode
std::unique_ptr< weld::Button > m_xUnProtectPB
RET_OK
RET_NO
RET_YES