Use COMReference to handle COM pointers in CreateShortcut
[LibreOffice.git] / sfx2 / source / dialog / securitypage.cxx
blob99b8d2517f8b6999175685f309fd95c16f9575f8
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 SfxPoolItemHolder aResult;
55 const SfxItemState nState(pViewSh->GetDispatcher()->QueryState(_nSlot, aResult));
56 bRet = SfxItemState::DEFAULT <= nState;
57 if (bRet)
58 _rValue = static_cast<const SfxBoolItem*>(aResult.getItem())->GetValue();
60 return bRet;
64 bool QueryRecordChangesProtectionState( RedliningMode _eMode, bool& _rValue )
66 bool bRet = false;
67 if (_eMode != RL_NONE)
69 TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_PROTECT : SID_CHG_PROTECT;
70 bRet = QueryState( nSlot, _rValue );
72 return bRet;
76 bool QueryRecordChangesState( RedliningMode _eMode, bool& _rValue )
78 bool bRet = false;
79 if (_eMode != RL_NONE)
81 TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_ON : FID_CHG_RECORD;
82 bRet = QueryState( nSlot, _rValue );
84 return bRet;
89 static bool lcl_GetPassword(
90 weld::Window *pParent,
91 bool bProtect,
92 /*out*/OUString &rPassword )
94 bool bRes = false;
95 SfxPasswordDialog aPasswdDlg(pParent);
96 aPasswdDlg.SetMinLen(1);
97 if (bProtect)
98 aPasswdDlg.ShowExtras( SfxShowExtras::CONFIRM );
99 if (RET_OK == aPasswdDlg.run() && !aPasswdDlg.GetPassword().isEmpty())
101 rPassword = aPasswdDlg.GetPassword();
102 bRes = true;
104 return bRes;
108 static bool lcl_IsPasswordCorrect(weld::Window *pParent, std::u16string_view rPassword)
110 SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
111 if (!pCurDocShell)
112 return false;
114 bool bRes = false;
115 uno::Sequence< sal_Int8 > aPasswordHash;
116 pCurDocShell->GetProtectionHash( aPasswordHash );
118 // check if supplied password was correct
119 if (aPasswordHash.getLength() == 1 && aPasswordHash[0] == 1)
121 // dummy RedlinePassword from OOXML import: get real password info
122 // from the grab-bag to verify the password
123 const css::uno::Sequence< css::beans::PropertyValue > aDocumentProtection =
124 pCurDocShell->GetDocumentProtectionFromGrabBag();
125 bRes =
126 // password is ok, if there is no DocumentProtection in the GrabBag,
127 // i.e. the dummy RedlinePassword imported from an OpenDocument file
128 !aDocumentProtection.hasElements() ||
129 // verify password with the password info imported from OOXML
130 ::comphelper::DocPasswordHelper::IsModifyPasswordCorrect( rPassword,
131 ::comphelper::DocPasswordHelper::ConvertPasswordInfo ( aDocumentProtection ) );
133 else
135 uno::Sequence< sal_Int8 > aNewPasswd( aPasswordHash );
136 SvPasswordHelper::GetHashPassword( aNewPasswd, rPassword );
137 bRes = SvPasswordHelper::CompareHashPassword( aPasswordHash, rPassword );
140 if ( !bRes )
142 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
143 VclMessageType::Info, VclButtonsType::Ok,
144 SfxResId(RID_SVXSTR_INCORRECT_PASSWORD)));
145 xInfoBox->run();
148 return bRes;
151 struct SfxSecurityPage_Impl
153 SfxSecurityPage & m_rMyTabPage;
155 RedliningMode m_eRedlingMode; // for record changes
157 bool m_bOrigPasswordIsConfirmed;
158 bool m_bNewPasswordIsValid;
159 OUString m_aNewPassword;
161 OUString m_aEndRedliningWarning;
162 bool m_bEndRedliningWarningDone;
164 std::unique_ptr<weld::CheckButton> m_xOpenReadonlyCB;
165 std::unique_ptr<weld::CheckButton> m_xRecordChangesCB; // for record changes
166 std::unique_ptr<weld::Button> m_xProtectPB; // for record changes
167 std::unique_ptr<weld::Button> m_xUnProtectPB; // for record changes
169 DECL_LINK(RecordChangesCBToggleHdl, weld::Toggleable&, void);
170 DECL_LINK(ChangeProtectionPBHdl, weld::Button&, void);
172 SfxSecurityPage_Impl( SfxSecurityPage &rDlg );
174 bool FillItemSet_Impl();
175 void Reset_Impl();
178 SfxSecurityPage_Impl::SfxSecurityPage_Impl(SfxSecurityPage &rTabPage)
179 : m_rMyTabPage(rTabPage)
180 , m_eRedlingMode(RL_NONE)
181 , m_bOrigPasswordIsConfirmed(false)
182 , m_bNewPasswordIsValid(false)
183 , m_aEndRedliningWarning(SfxResId(RID_SVXSTR_END_REDLINING_WARNING))
184 , m_bEndRedliningWarningDone(false)
185 , m_xOpenReadonlyCB(rTabPage.GetBuilder().weld_check_button(u"readonly"_ustr))
186 , m_xRecordChangesCB(rTabPage.GetBuilder().weld_check_button(u"recordchanges"_ustr))
187 , m_xProtectPB(rTabPage.GetBuilder().weld_button(u"protect"_ustr))
188 , m_xUnProtectPB(rTabPage.GetBuilder().weld_button(u"unprotect"_ustr))
190 m_xProtectPB->show();
191 m_xUnProtectPB->hide();
193 m_xRecordChangesCB->connect_toggled(LINK(this, SfxSecurityPage_Impl, RecordChangesCBToggleHdl));
194 m_xProtectPB->connect_clicked(LINK(this, SfxSecurityPage_Impl, ChangeProtectionPBHdl));
195 m_xUnProtectPB->connect_clicked(LINK(this, SfxSecurityPage_Impl, ChangeProtectionPBHdl));
198 bool SfxSecurityPage_Impl::FillItemSet_Impl()
200 bool bModified = false;
202 SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
203 if (pCurDocShell && !pCurDocShell->IsReadOnly())
205 if (m_eRedlingMode != RL_NONE )
207 const bool bDoRecordChanges = m_xRecordChangesCB->get_active();
208 const bool bDoChangeProtection = m_xUnProtectPB->get_visible();
210 // sanity checks
211 DBG_ASSERT( bDoRecordChanges || !bDoChangeProtection, "no change recording should imply no change protection" );
212 DBG_ASSERT( bDoChangeProtection || !bDoRecordChanges, "no change protection should imply no change recording" );
213 DBG_ASSERT( !bDoChangeProtection || !m_aNewPassword.isEmpty(), "change protection should imply password length is > 0" );
214 DBG_ASSERT( bDoChangeProtection || m_aNewPassword.isEmpty(), "no change protection should imply password length is 0" );
216 // change recording
217 if (bDoRecordChanges != pCurDocShell->IsChangeRecording())
219 pCurDocShell->SetChangeRecording( bDoRecordChanges );
220 bModified = true;
223 // change record protection
224 if (m_bNewPasswordIsValid &&
225 bDoChangeProtection != pCurDocShell->HasChangeRecordProtection())
227 DBG_ASSERT( !bDoChangeProtection || bDoRecordChanges,
228 "change protection requires record changes to be active!" );
229 pCurDocShell->SetProtectionPassword( m_aNewPassword );
230 bModified = true;
234 // open read-only?
235 const bool bDoOpenReadonly = m_xOpenReadonlyCB->get_active();
236 if (bDoOpenReadonly != pCurDocShell->IsSecurityOptOpenReadOnly())
238 pCurDocShell->SetSecurityOptOpenReadOnly( bDoOpenReadonly );
239 bModified = true;
243 return bModified;
247 void SfxSecurityPage_Impl::Reset_Impl()
249 SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
251 if (!pCurDocShell)
253 // no doc -> hide document settings
254 m_xOpenReadonlyCB->set_sensitive(false);
255 m_xRecordChangesCB->set_sensitive(false);
256 m_xProtectPB->show();
257 m_xProtectPB->set_sensitive(false);
258 m_xUnProtectPB->hide();
259 m_xUnProtectPB->set_sensitive(false);
261 else
263 bool bIsHTMLDoc = false;
264 bool bProtect = true, bUnProtect = false;
265 SfxViewShell* pViewSh = SfxViewShell::Current();
266 if (pViewSh)
268 SfxPoolItemHolder aResult;
270 if (SfxItemState::DEFAULT <= pViewSh->GetDispatcher()->QueryState(SID_HTML_MODE, aResult))
272 const SfxUInt16Item* pItem(static_cast<const SfxUInt16Item*>(aResult.getItem()));
273 const 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, u"sfx/ui/securityinfopage.ui"_ustr, u"SecurityInfoPage"_ustr, &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: */