1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
;
46 enum RedliningMode
{ RL_NONE
, RL_WRITER
, RL_CALC
};
48 bool QueryState( TypedWhichId
<SfxBoolItem
> _nSlot
, bool& _rValue
)
51 SfxViewShell
* pViewSh
= SfxViewShell::Current();
54 SfxPoolItemHolder aResult
;
55 const SfxItemState
nState(pViewSh
->GetDispatcher()->QueryState(_nSlot
, aResult
));
56 bRet
= SfxItemState::DEFAULT
<= nState
;
58 _rValue
= static_cast<const SfxBoolItem
*>(aResult
.getItem())->GetValue();
64 bool QueryRecordChangesProtectionState( RedliningMode _eMode
, bool& _rValue
)
67 if (_eMode
!= RL_NONE
)
69 TypedWhichId
<SfxBoolItem
> nSlot
= _eMode
== RL_WRITER
? FN_REDLINE_PROTECT
: SID_CHG_PROTECT
;
70 bRet
= QueryState( nSlot
, _rValue
);
76 bool QueryRecordChangesState( RedliningMode _eMode
, bool& _rValue
)
79 if (_eMode
!= RL_NONE
)
81 TypedWhichId
<SfxBoolItem
> nSlot
= _eMode
== RL_WRITER
? FN_REDLINE_ON
: FID_CHG_RECORD
;
82 bRet
= QueryState( nSlot
, _rValue
);
89 static bool lcl_GetPassword(
90 weld::Window
*pParent
,
92 /*out*/OUString
&rPassword
)
95 SfxPasswordDialog
aPasswdDlg(pParent
);
96 aPasswdDlg
.SetMinLen(1);
98 aPasswdDlg
.ShowExtras( SfxShowExtras::CONFIRM
);
99 if (RET_OK
== aPasswdDlg
.run() && !aPasswdDlg
.GetPassword().isEmpty())
101 rPassword
= aPasswdDlg
.GetPassword();
108 static bool lcl_IsPasswordCorrect(weld::Window
*pParent
, std::u16string_view rPassword
)
110 SfxObjectShell
* pCurDocShell
= SfxObjectShell::Current();
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();
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
) );
135 uno::Sequence
< sal_Int8
> aNewPasswd( aPasswordHash
);
136 SvPasswordHelper::GetHashPassword( aNewPasswd
, rPassword
);
137 bRes
= SvPasswordHelper::CompareHashPassword( aPasswordHash
, rPassword
);
142 std::unique_ptr
<weld::MessageDialog
> xInfoBox(Application::CreateMessageDialog(pParent
,
143 VclMessageType::Info
, VclButtonsType::Ok
,
144 SfxResId(RID_SVXSTR_INCORRECT_PASSWORD
)));
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();
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();
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" );
217 if (bDoRecordChanges
!= pCurDocShell
->IsChangeRecording())
219 pCurDocShell
->SetChangeRecording( bDoRecordChanges
);
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
);
235 const bool bDoOpenReadonly
= m_xOpenReadonlyCB
->get_active();
236 if (bDoOpenReadonly
!= pCurDocShell
->IsSecurityOptOpenReadOnly())
238 pCurDocShell
->SetSecurityOptOpenReadOnly( bDoOpenReadonly
);
247 void SfxSecurityPage_Impl::Reset_Impl()
249 SfxObjectShell
* pCurDocShell
= SfxObjectShell::Current();
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);
263 bool bIsHTMLDoc
= false;
264 bool bProtect
= true, bUnProtect
= false;
265 SfxViewShell
* pViewSh
= SfxViewShell::Current();
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();
281 m_xOpenReadonlyCB
->set_active(pCurDocShell
->IsSecurityOptOpenReadOnly());
282 m_xOpenReadonlyCB
->set_sensitive(!bIsReadonly
);
285 m_xOpenReadonlyCB
->set_sensitive(false);
288 if (QueryRecordChangesState( RL_WRITER
, bRecordChanges
) && !bIsHTMLDoc
)
289 m_eRedlingMode
= RL_WRITER
;
290 else if (QueryRecordChangesState( RL_CALC
, bRecordChanges
))
291 m_eRedlingMode
= RL_CALC
;
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
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
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 '!'
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
)
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
))
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;
372 m_xRecordChangesCB
->set_active(true); // restore original state
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
)
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
;
398 // ask for password and if dialog is canceled or no password provided return
399 if (!lcl_GetPassword(m_rMyTabPage
.GetFrameWeld(), bNewProtection
, aPasswordText
))
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;
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();
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: */