Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sfx2 / source / dialog / securitypage.cxx
blob6dfb94d3da2ff536c9193a79b9646f5e89283919
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sfx2/htmlmode.hxx>
22 #include <sfx2/sfxresid.hxx>
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>
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>
35 #include <svl/PasswordHelper.hxx>
36 #include <comphelper/docpasswordhelper.hxx>
38 #include <sfx2/strings.hrc>
40 #include "securitypage.hxx"
42 using namespace ::com::sun::star;
44 namespace
46 enum RedliningMode { RL_NONE, RL_WRITER, RL_CALC };
48 bool QueryState( TypedWhichId<SfxBoolItem> _nSlot, bool& _rValue )
50 bool bRet = false;
51 SfxViewShell* pViewSh = SfxViewShell::Current();
52 if (pViewSh)
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();
61 return bRet;
65 bool QueryRecordChangesProtectionState( RedliningMode _eMode, bool& _rValue )
67 bool bRet = false;
68 if (_eMode != RL_NONE)
70 TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_PROTECT : SID_CHG_PROTECT;
71 bRet = QueryState( nSlot, _rValue );
73 return bRet;
77 bool QueryRecordChangesState( RedliningMode _eMode, bool& _rValue )
79 bool bRet = false;
80 if (_eMode != RL_NONE)
82 TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_ON : FID_CHG_RECORD;
83 bRet = QueryState( nSlot, _rValue );
85 return bRet;
90 static bool lcl_GetPassword(
91 weld::Window *pParent,
92 bool bProtect,
93 /*out*/OUString &rPassword )
95 bool bRes = false;
96 SfxPasswordDialog aPasswdDlg(pParent);
97 aPasswdDlg.SetMinLen(1);
98 if (bProtect)
99 aPasswdDlg.ShowExtras( SfxShowExtras::CONFIRM );
100 if (RET_OK == aPasswdDlg.run() && !aPasswdDlg.GetPassword().isEmpty())
102 rPassword = aPasswdDlg.GetPassword();
103 bRes = true;
105 return bRes;
109 static bool lcl_IsPasswordCorrect(weld::Window *pParent, std::u16string_view rPassword)
111 SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
112 if (!pCurDocShell)
113 return false;
115 bool bRes = false;
116 uno::Sequence< sal_Int8 > aPasswordHash;
117 pCurDocShell->GetProtectionHash( aPasswordHash );
119 // check if supplied password was correct
120 if (aPasswordHash.getLength() == 1 && aPasswordHash[0] == 1)
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
131 ::comphelper::DocPasswordHelper::IsModifyPasswordCorrect( rPassword,
132 ::comphelper::DocPasswordHelper::ConvertPasswordInfo ( aDocumentProtection ) );
134 else
136 uno::Sequence< sal_Int8 > aNewPasswd( aPasswordHash );
137 SvPasswordHelper::GetHashPassword( aNewPasswd, rPassword );
138 bRes = SvPasswordHelper::CompareHashPassword( aPasswordHash, rPassword );
141 if ( !bRes )
143 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
144 VclMessageType::Info, VclButtonsType::Ok,
145 SfxResId(RID_SVXSTR_INCORRECT_PASSWORD)));
146 xInfoBox->run();
149 return bRes;
152 struct SfxSecurityPage_Impl
154 SfxSecurityPage & m_rMyTabPage;
156 RedliningMode m_eRedlingMode; // for record changes
158 bool m_bOrigPasswordIsConfirmed;
159 bool m_bNewPasswordIsValid;
160 OUString m_aNewPassword;
162 OUString m_aEndRedliningWarning;
163 bool m_bEndRedliningWarningDone;
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
170 DECL_LINK(RecordChangesCBToggleHdl, weld::Toggleable&, void);
171 DECL_LINK(ChangeProtectionPBHdl, weld::Button&, void);
173 SfxSecurityPage_Impl( SfxSecurityPage &rDlg );
175 bool FillItemSet_Impl();
176 void Reset_Impl();
179 SfxSecurityPage_Impl::SfxSecurityPage_Impl(SfxSecurityPage &rTabPage)
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"))
191 m_xProtectPB->show();
192 m_xUnProtectPB->hide();
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));
199 bool SfxSecurityPage_Impl::FillItemSet_Impl()
201 bool bModified = false;
203 SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
204 if (pCurDocShell && !pCurDocShell->IsReadOnly())
206 if (m_eRedlingMode != RL_NONE )
208 const bool bDoRecordChanges = m_xRecordChangesCB->get_active();
209 const bool bDoChangeProtection = m_xUnProtectPB->get_visible();
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" );
217 // change recording
218 if (bDoRecordChanges != pCurDocShell->IsChangeRecording())
220 pCurDocShell->SetChangeRecording( bDoRecordChanges );
221 bModified = true;
224 // change record protection
225 if (m_bNewPasswordIsValid &&
226 bDoChangeProtection != pCurDocShell->HasChangeRecordProtection())
228 DBG_ASSERT( !bDoChangeProtection || bDoRecordChanges,
229 "change protection requires record changes to be active!" );
230 pCurDocShell->SetProtectionPassword( m_aNewPassword );
231 bModified = true;
235 // open read-only?
236 const bool bDoOpenReadonly = m_xOpenReadonlyCB->get_active();
237 if (bDoOpenReadonly != pCurDocShell->IsSecurityOptOpenReadOnly())
239 pCurDocShell->SetSecurityOptOpenReadOnly( bDoOpenReadonly );
240 bModified = true;
244 return bModified;
248 void SfxSecurityPage_Impl::Reset_Impl()
250 SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
252 if (!pCurDocShell)
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);
262 else
264 bool bIsHTMLDoc = false;
265 bool bProtect = true, bUnProtect = false;
266 SfxViewShell* pViewSh = SfxViewShell::Current();
267 if (pViewSh)
269 const SfxUInt16Item* pItem;
270 SfxDispatcher* pDisp = pViewSh->GetDispatcher();
271 if (SfxItemState::DEFAULT <= pDisp->QueryState( SID_HTML_MODE, pItem ))
273 sal_uInt16 nMode = pItem->GetValue();
274 bIsHTMLDoc = ( ( nMode & HTMLMODE_ON ) != 0 );
278 bool bIsReadonly = pCurDocShell->IsReadOnly();
279 if (!bIsHTMLDoc)
281 m_xOpenReadonlyCB->set_active(pCurDocShell->IsSecurityOptOpenReadOnly());
282 m_xOpenReadonlyCB->set_sensitive(!bIsReadonly);
284 else
285 m_xOpenReadonlyCB->set_sensitive(false);
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;
295 if (m_eRedlingMode != RL_NONE)
297 bool bProtection(false);
298 QueryRecordChangesProtectionState( m_eRedlingMode, bProtection );
300 m_xProtectPB->set_sensitive(!bIsReadonly);
301 m_xUnProtectPB->set_sensitive(!bIsReadonly);
302 // set the right text
303 if (bProtection)
305 bProtect = false;
306 bUnProtect = true;
309 m_xRecordChangesCB->set_active(bRecordChanges);
310 m_xRecordChangesCB->set_sensitive(/*!bProtection && */!bIsReadonly);
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
319 else
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);
330 m_xProtectPB->set_visible(bProtect);
331 m_xUnProtectPB->set_visible(bUnProtect);
335 IMPL_LINK_NOARG(SfxSecurityPage_Impl, RecordChangesCBToggleHdl, weld::Toggleable&, void)
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;
341 bool bAlreadyDone = false;
342 if (!m_bEndRedliningWarningDone)
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;
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)
358 OUString aPasswordText;
360 // dialog canceled or no password provided
361 if (!lcl_GetPassword( m_rMyTabPage.GetFrameWeld(), false, aPasswordText ))
362 bAlreadyDone = true;
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;
371 if (bAlreadyDone)
372 m_xRecordChangesCB->set_active(true); // restore original state
373 else
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();
384 IMPL_LINK_NOARG(SfxSecurityPage_Impl, ChangeProtectionPBHdl, weld::Button&, void)
386 if (m_eRedlingMode == RL_NONE)
387 return;
389 // the push button text is always the opposite of the current state. Thus:
390 const bool bCurrentProtection = m_xUnProtectPB->get_visible();
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)
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;
402 // provided password still needs to be checked?
403 if (!bNewProtection && !m_bOrigPasswordIsConfirmed)
405 if (lcl_IsPasswordCorrect(m_rMyTabPage.GetFrameWeld(), aPasswordText))
406 m_bOrigPasswordIsConfirmed = true;
407 else
408 return;
411 DBG_ASSERT( m_bOrigPasswordIsConfirmed, "ooops... this should not have happened!" );
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();
418 m_xRecordChangesCB->set_active(bNewProtection);
420 m_xUnProtectPB->set_visible(bNewProtection);
421 m_xProtectPB->set_visible(!bNewProtection);
424 std::unique_ptr<SfxTabPage> SfxSecurityPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet * rItemSet)
426 return std::make_unique<SfxSecurityPage>(pPage, pController, *rItemSet);
429 SfxSecurityPage::SfxSecurityPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet)
430 : SfxTabPage(pPage, pController, "sfx/ui/securityinfopage.ui", "SecurityInfoPage", &rItemSet)
432 m_pImpl.reset(new SfxSecurityPage_Impl( *this ));
435 bool SfxSecurityPage::FillItemSet( SfxItemSet * /*rItemSet*/ )
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;
444 void SfxSecurityPage::Reset( const SfxItemSet * /*rItemSet*/ )
446 DBG_ASSERT(m_pImpl, "implementation pointer is 0. Still in c-tor?");
447 if (m_pImpl != nullptr)
448 m_pImpl->Reset_Impl();
451 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */