xref: /core/sfx2/source/dialog/securitypage.cxx (revision ca5c9591ba38ad83415a2d4ced98bfc74d30b032)
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>
35 #include <svl/PasswordHelper.hxx>
36 #include <comphelper/docpasswordhelper.hxx>
37 
38 #include <sfx2/strings.hrc>
39 
40 #include "securitypage.hxx"
41 
42 using namespace ::com::sun::star;
43 
44 namespace
45 {
46     enum RedliningMode  { RL_NONE, RL_WRITER, RL_CALC };
47 
QueryState(TypedWhichId<SfxBoolItem> _nSlot,bool & _rValue)48     bool QueryState( TypedWhichId<SfxBoolItem> _nSlot, bool& _rValue )
49     {
50         bool bRet = false;
51         SfxViewShell* pViewSh = SfxViewShell::Current();
52         if (pViewSh)
53         {
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();
59         }
60         return bRet;
61     }
62 
63 
QueryRecordChangesProtectionState(RedliningMode _eMode,bool & _rValue)64     bool QueryRecordChangesProtectionState( RedliningMode _eMode, bool& _rValue )
65     {
66         bool bRet = false;
67         if (_eMode != RL_NONE)
68         {
69             TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_PROTECT : SID_CHG_PROTECT;
70             bRet = QueryState( nSlot, _rValue );
71         }
72         return bRet;
73     }
74 
75 
QueryRecordChangesState(RedliningMode _eMode,bool & _rValue)76     bool QueryRecordChangesState( RedliningMode _eMode, bool& _rValue )
77     {
78         bool bRet = false;
79         if (_eMode != RL_NONE)
80         {
81             TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_ON : FID_CHG_RECORD;
82             bRet = QueryState( nSlot, _rValue );
83         }
84         return bRet;
85     }
86 }
87 
88 
lcl_GetPassword(weld::Window * pParent,bool bProtect,OUString & rPassword)89 static bool lcl_GetPassword(
90     weld::Window *pParent,
91     bool bProtect,
92     /*out*/OUString &rPassword )
93 {
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())
100     {
101         rPassword = aPasswdDlg.GetPassword();
102         bRes = true;
103     }
104     return bRes;
105 }
106 
107 
lcl_IsPasswordCorrect(weld::Window * pParent,std::u16string_view rPassword)108 static bool lcl_IsPasswordCorrect(weld::Window *pParent, std::u16string_view rPassword)
109 {
110     SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
111     if (!pCurDocShell)
112         return false;
113 
114     bool bRes = false;
115     uno::Sequence< sal_Int8 >   aPasswordHash;
116     pCurDocShell->GetProtectionHash( aPasswordHash );
117 
118     // check if supplied password was correct
119     if (aPasswordHash.getLength() == 1 && aPasswordHash[0] == 1)
120     {
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 ) );
132     }
133     else
134     {
135         uno::Sequence< sal_Int8 > aNewPasswd( aPasswordHash );
136         SvPasswordHelper::GetHashPassword( aNewPasswd, rPassword );
137         bRes = SvPasswordHelper::CompareHashPassword( aPasswordHash, rPassword );
138     }
139 
140     if ( !bRes )
141     {
142         std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
143                                                       VclMessageType::Info, VclButtonsType::Ok,
144                                                       SfxResId(RID_SVXSTR_INCORRECT_PASSWORD)));
145         xInfoBox->run();
146     }
147 
148     return bRes;
149 }
150 
151 struct SfxSecurityPage_Impl
152 {
153     SfxSecurityPage &   m_rMyTabPage;
154 
155     RedliningMode       m_eRedlingMode;             // for record changes
156 
157     bool                m_bOrigPasswordIsConfirmed;
158     bool                m_bNewPasswordIsValid;
159     OUString            m_aNewPassword;
160 
161     OUString            m_aEndRedliningWarning;
162     bool                m_bEndRedliningWarningDone;
163 
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
168 
169     DECL_LINK(RecordChangesCBToggleHdl, weld::Toggleable&, void);
170     DECL_LINK(ChangeProtectionPBHdl, weld::Button&, void);
171 
172     SfxSecurityPage_Impl( SfxSecurityPage &rDlg );
173 
174     bool    FillItemSet_Impl();
175     void    Reset_Impl();
176 };
177 
SfxSecurityPage_Impl(SfxSecurityPage & rTabPage)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))
189 {
190     m_xProtectPB->show();
191     m_xUnProtectPB->hide();
192 
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));
196 }
197 
FillItemSet_Impl()198 bool SfxSecurityPage_Impl::FillItemSet_Impl()
199 {
200     bool bModified = false;
201 
202     SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
203     if (pCurDocShell && !pCurDocShell->IsReadOnly())
204     {
205         if (m_eRedlingMode != RL_NONE )
206         {
207             const bool bDoRecordChanges = m_xRecordChangesCB->get_active();
208             const bool bDoChangeProtection  = m_xUnProtectPB->get_visible();
209 
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" );
215 
216             // change recording
217             if (bDoRecordChanges != pCurDocShell->IsChangeRecording())
218             {
219                 pCurDocShell->SetChangeRecording( bDoRecordChanges );
220                 bModified = true;
221             }
222 
223             // change record protection
224             if (m_bNewPasswordIsValid &&
225                 bDoChangeProtection != pCurDocShell->HasChangeRecordProtection())
226             {
227                 DBG_ASSERT( !bDoChangeProtection || bDoRecordChanges,
228                         "change protection requires record changes to be active!" );
229                 pCurDocShell->SetProtectionPassword( m_aNewPassword );
230                 bModified = true;
231             }
232         }
233 
234         // open read-only?
235         const bool bDoOpenReadonly = m_xOpenReadonlyCB->get_active();
236         if (bDoOpenReadonly != pCurDocShell->IsSecurityOptOpenReadOnly())
237         {
238             pCurDocShell->SetSecurityOptOpenReadOnly( bDoOpenReadonly );
239             bModified = true;
240         }
241     }
242 
243     return bModified;
244 }
245 
246 
Reset_Impl()247 void SfxSecurityPage_Impl::Reset_Impl()
248 {
249     SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
250 
251     if (!pCurDocShell)
252     {
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);
260     }
261     else
262     {
263         bool bIsHTMLDoc = false;
264         bool bProtect = true, bUnProtect = false;
265         SfxViewShell* pViewSh = SfxViewShell::Current();
266         if (pViewSh)
267         {
268             SfxPoolItemHolder aResult;
269 
270             if (SfxItemState::DEFAULT <= pViewSh->GetDispatcher()->QueryState(SID_HTML_MODE, aResult))
271             {
272                 const SfxUInt16Item* pItem(static_cast<const SfxUInt16Item*>(aResult.getItem()));
273                 const 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(/*!bProtection && */!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 
IMPL_LINK_NOARG(SfxSecurityPage_Impl,RecordChangesCBToggleHdl,weld::Toggleable &,void)335 IMPL_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 
IMPL_LINK_NOARG(SfxSecurityPage_Impl,ChangeProtectionPBHdl,weld::Button &,void)384 IMPL_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 
Create(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet * rItemSet)424 std::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 
SfxSecurityPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & 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)
431 {
432     m_pImpl.reset(new SfxSecurityPage_Impl( *this ));
433 }
434 
FillItemSet(SfxItemSet *)435 bool SfxSecurityPage::FillItemSet( SfxItemSet * /*rItemSet*/ )
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 
Reset(const SfxItemSet *)444 void 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: */
452