xref: /trunk/main/sfx2/source/dialog/securitypage.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sfx2.hxx"
30 
31 #include "sfx2/securitypage.hxx"
32 
33 #include "securitypage.hrc"
34 #include "sfxresid.hxx"
35 
36 #include <sfx2/sfx.hrc>
37 #include <sfx2/sfxsids.hrc>
38 #include <sfx2/objsh.hxx>
39 #include <sfx2/viewsh.hxx>
40 #include <sfx2/dispatch.hxx>
41 #include <sfx2/passwd.hxx>
42 
43 #include <vcl/button.hxx>
44 #include <vcl/edit.hxx>
45 #include <vcl/fixed.hxx>
46 #include <vcl/msgbox.hxx>
47 #include <svl/eitem.hxx>
48 #include <svl/poolitem.hxx>
49 #include <svl/intitem.hxx>
50 #include <svl/PasswordHelper.hxx>
51 #include <svtools/xwindowitem.hxx>
52 
53 
54 using namespace ::com::sun::star;
55 
56 //////////////////////////////////////////////////////////////////////
57 
58 
59 namespace
60 {
61     enum RedliningMode  { RL_NONE, RL_WRITER, RL_CALC };
62     enum RedlineFunc    { RF_ON, RF_PROTECT };
63 
64 /*
65     bool QueryIsEnabled( sal_uInt16 _nSlot )
66     {
67         bool bRes = false;
68         SfxViewShell* pViewSh = SfxViewShell::Current();
69         if (pViewSh)
70         {
71             const SfxPoolItem* pItem;
72             SfxDispatcher* pDisp = pViewSh->GetDispatcher();
73             SfxItemState eState = pDisp->QueryState( _nSlot, pItem );
74             bRes = (eState & SFX_ITEM_DISABLED) == 0;
75         }
76         return bRes;
77     }
78 */
79 
80     bool QueryState( sal_uInt16 _nSlot, bool& _rValue )
81     {
82         bool bRet = false;
83         SfxViewShell* pViewSh = SfxViewShell::Current();
84         if (pViewSh)
85         {
86             const SfxPoolItem* pItem;
87             SfxDispatcher* pDisp = pViewSh->GetDispatcher();
88             SfxItemState nState = pDisp->QueryState( _nSlot, pItem );
89             bRet = SFX_ITEM_AVAILABLE <= nState;
90             if (bRet)
91                 _rValue = ( static_cast< const SfxBoolItem* >( pItem ) )->GetValue();
92         }
93         return bRet;
94     }
95 
96 
97     bool QueryRecordChangesProtectionState( RedliningMode _eMode, bool& _rValue )
98     {
99         bool bRet = false;
100         if (_eMode != RL_NONE)
101         {
102             sal_uInt16 nSlot = _eMode == RL_WRITER ? FN_REDLINE_PROTECT : SID_CHG_PROTECT;
103             bRet = QueryState( nSlot, _rValue );
104         }
105         return bRet;
106     }
107 
108 
109     bool QueryRecordChangesState( RedliningMode _eMode, bool& _rValue )
110     {
111         bool bRet = false;
112         if (_eMode != RL_NONE)
113         {
114             sal_uInt16 nSlot = _eMode == RL_WRITER ? FN_REDLINE_ON : FID_CHG_RECORD;
115             bRet = QueryState( nSlot, _rValue );
116         }
117         return bRet;
118     }
119 }
120 
121 
122 //////////////////////////////////////////////////////////////////////
123 
124 
125 static short lcl_GetPassword(
126     Window *pParent,
127     bool bProtect,
128     /*out*/String &rPassword )
129 {
130     bool bRes = false;
131     SfxPasswordDialog aPasswdDlg( pParent );
132     const String aTitle( SfxResId( bProtect ? RID_SFX_PROTECT_RECORDS : RID_SFX_UNPROTECT_RECORDS ) );
133     aPasswdDlg.SetText( aTitle );
134     aPasswdDlg.SetMinLen( 1 );
135     if (bProtect)
136         aPasswdDlg.ShowExtras( SHOWEXTRAS_CONFIRM );
137     if (RET_OK == aPasswdDlg.Execute() && aPasswdDlg.GetPassword().Len() > 0)
138     {
139         rPassword = aPasswdDlg.GetPassword();
140         bRes = true;
141     }
142     return bRes;
143 }
144 
145 
146 static bool lcl_IsPasswordCorrect( const String &rPassword )
147 {
148     bool bRes = false;
149 
150     SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
151     uno::Sequence< sal_Int8 >   aPasswordHash;
152     pCurDocShell->GetProtectionHash( aPasswordHash );
153 
154     // check if supplied password was correct
155     uno::Sequence< sal_Int8 > aNewPasswd( aPasswordHash );
156     SvPasswordHelper::GetHashPassword( aNewPasswd, rPassword );
157     if (SvPasswordHelper::CompareHashPassword( aPasswordHash, rPassword ))
158         bRes = true;    // password was correct
159     else
160         InfoBox( NULL, String( SfxResId( RID_SFX_INCORRECT_PASSWORD ) ) ).Execute();
161 
162     return bRes;
163 }
164 
165 
166 //////////////////////////////////////////////////////////////////////
167 
168 
169 struct SfxSecurityPage_Impl
170 {
171     SfxSecurityPage &   m_rMyTabPage;
172 
173     FixedLine           m_aNewPasswordToOpenFL;
174     FixedText           m_aNewPasswordToOpenFT;
175     Edit                m_aNewPasswordToOpenED;
176     FixedText           m_aConfirmPasswordToOpenFT;
177     Edit                m_aConfirmPasswordToOpenED;
178     FixedText           m_aNewPasswordInfoFT;
179 
180     FixedLine           m_aNewPasswordToModifyFL;
181     FixedText           m_aNewPasswordToModifyFT;
182     Edit                m_aNewPasswordToModifyED;
183     FixedText           m_aConfirmPasswordToModifyFT;
184     Edit                m_aConfirmPasswordToModifyED;
185 
186     FixedLine           m_aOptionsFL;
187     CheckBox            m_aOpenReadonlyCB;
188     CheckBox            m_aRecordChangesCB;         // for record changes
189     PushButton          m_aChangeProtectionPB;      // for record changes
190     String              m_aProtectSTR;              // for record changes
191     String              m_aUnProtectSTR;            // for record changes
192     RedliningMode       m_eRedlingMode;             // for record changes
193 
194     bool                m_bOrigPasswordIsConfirmed;
195     bool                m_bNewPasswordIsValid;
196     String              m_aNewPassword;
197 
198     String              m_aEndRedliningWarning;
199     bool                m_bEndRedliningWarningDone;
200 
201     DECL_LINK( RecordChangesCBToggleHdl, void* );
202     DECL_LINK( ChangeProtectionPBHdl, void* );
203 
204     SfxSecurityPage_Impl( SfxSecurityPage &rDlg, const SfxItemSet &rItemSet );
205     ~SfxSecurityPage_Impl();
206 
207     sal_Bool    FillItemSet_Impl( SfxItemSet & );
208     void    Reset_Impl( const SfxItemSet & );
209 };
210 
211 
212 SfxSecurityPage_Impl::SfxSecurityPage_Impl( SfxSecurityPage &rTabPage, const SfxItemSet & ) :
213     m_rMyTabPage                    (rTabPage),
214     m_aNewPasswordToOpenFL          (&rTabPage, SfxResId( PASSWORD_TO_OPEN_FL ) ),
215     m_aNewPasswordToOpenFT          (&rTabPage, SfxResId( PASSWORD_TO_OPEN_FT ) ),
216     m_aNewPasswordToOpenED          (&rTabPage, SfxResId( PASSWORD_TO_OPEN_ED ) ),
217     m_aConfirmPasswordToOpenFT      (&rTabPage, SfxResId( CONFIRM_PASSWORD_TO_OPEN_FT ) ),
218     m_aConfirmPasswordToOpenED      (&rTabPage, SfxResId( CONFIRM_PASSWORD_TO_OPEN_ED ) ),
219     m_aNewPasswordInfoFT            (&rTabPage, SfxResId( PASSWORD_INFO_FT ) ),
220     m_aNewPasswordToModifyFL        (&rTabPage, SfxResId( PASSWORD_TO_MODIFY_FL ) ),
221     m_aNewPasswordToModifyFT        (&rTabPage, SfxResId( PASSWORD_TO_MODIFY_FT ) ),
222     m_aNewPasswordToModifyED        (&rTabPage, SfxResId( PASSWORD_TO_MODIFY_ED ) ),
223     m_aConfirmPasswordToModifyFT    (&rTabPage, SfxResId( CONFIRM_PASSWORD_TO_MODIFY_FT ) ),
224     m_aConfirmPasswordToModifyED    (&rTabPage, SfxResId( CONFIRM_PASSWORD_TO_MODIFY_ED ) ),
225     m_aOptionsFL                    (&rTabPage, SfxResId( OPTIONS_FL ) ),
226     m_aOpenReadonlyCB               (&rTabPage, SfxResId( OPEN_READONLY_CB ) ),
227     m_aRecordChangesCB              (&rTabPage, SfxResId( RECORD_CHANGES_CB ) ),
228     m_aChangeProtectionPB           (&rTabPage, SfxResId( CHANGE_PROTECTION_PB ) ),
229     m_aProtectSTR                   ( SfxResId( STR_PROTECT ) ),
230     m_aUnProtectSTR                 ( SfxResId( STR_UNPROTECT ) ),
231     m_eRedlingMode                  ( RL_NONE ),
232     m_bOrigPasswordIsConfirmed      ( false ),
233     m_bNewPasswordIsValid           ( false ),
234     m_aEndRedliningWarning          ( SfxResId( STR_END_REDLINING_WARNING ) ),
235     m_bEndRedliningWarningDone      ( false )
236 {
237     m_aChangeProtectionPB.SetText( m_aProtectSTR );
238     // adjust button width if necessary
239     long nBtnTextWidth = 0;
240     long nTemp = m_aChangeProtectionPB.GetCtrlTextWidth( m_aChangeProtectionPB.GetText() );
241     if (nTemp > nBtnTextWidth)
242         nBtnTextWidth = nTemp;
243 
244     // force toggle hdl called before visual change of checkbox
245     m_aRecordChangesCB.SetStyle( m_aRecordChangesCB.GetStyle() | WB_EARLYTOGGLE );
246     m_aRecordChangesCB.SetToggleHdl( LINK( this, SfxSecurityPage_Impl, RecordChangesCBToggleHdl ) );
247     m_aChangeProtectionPB.SetClickHdl( LINK( this, SfxSecurityPage_Impl, ChangeProtectionPBHdl ) );
248 
249 
250     // #i112277: for the time being (OOO 3.3) the following options should not
251     // be available. In the long run however it is planned to implement the yet
252     // missing functionality. Thus now we hide them and move the remaining ones up.
253     m_aNewPasswordToOpenFL.Hide();
254     m_aNewPasswordToOpenFT.Hide();
255     m_aNewPasswordToOpenED.Hide();
256     m_aConfirmPasswordToOpenFT.Hide();
257     m_aConfirmPasswordToOpenED.Hide();
258     m_aNewPasswordInfoFT.Hide();
259     m_aNewPasswordToModifyFL.Hide();
260     m_aNewPasswordToModifyFT.Hide();
261     m_aNewPasswordToModifyED.Hide();
262     m_aConfirmPasswordToModifyFT.Hide();
263     m_aConfirmPasswordToModifyED.Hide();
264     const long nDelta = m_aOptionsFL.GetPosPixel().Y() - m_aNewPasswordToOpenFL.GetPosPixel().Y();
265     Point aPos;
266     aPos = m_aOptionsFL.GetPosPixel();
267     aPos.Y() -= nDelta;
268     m_aOptionsFL.SetPosPixel( aPos );
269     aPos = m_aOpenReadonlyCB.GetPosPixel();
270     aPos.Y() -= nDelta;
271     m_aOpenReadonlyCB.SetPosPixel( aPos );
272     aPos = m_aRecordChangesCB.GetPosPixel();
273     aPos.Y() -= nDelta;
274     m_aRecordChangesCB.SetPosPixel( aPos );
275     aPos = m_aChangeProtectionPB.GetPosPixel();
276     aPos.Y() -= nDelta;
277     m_aChangeProtectionPB.SetPosPixel( aPos );
278 }
279 
280 
281 SfxSecurityPage_Impl::~SfxSecurityPage_Impl()
282 {
283 }
284 
285 
286 sal_Bool SfxSecurityPage_Impl::FillItemSet_Impl( SfxItemSet & )
287 {
288     bool bModified = false;
289 
290     SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
291     if (pCurDocShell&& !pCurDocShell->IsReadOnly())
292     {
293         if (m_eRedlingMode != RL_NONE )
294         {
295             const bool bDoRecordChanges     = m_aRecordChangesCB.IsChecked();
296             const bool bDoChangeProtection  = m_aChangeProtectionPB.GetText() != m_aProtectSTR;
297 
298             // sanity checks
299             DBG_ASSERT( bDoRecordChanges || !bDoChangeProtection, "no change recording should imply no change protection" );
300             DBG_ASSERT( bDoChangeProtection || !bDoRecordChanges, "no change protection should imply no change recording" );
301             DBG_ASSERT( !bDoChangeProtection || m_aNewPassword.Len() > 0, "change protection should imply password length is > 0" );
302             DBG_ASSERT( bDoChangeProtection || m_aNewPassword.Len() == 0, "no change protection should imply password length is 0" );
303 
304             // change recording
305             if (bDoRecordChanges != pCurDocShell->IsChangeRecording())
306             {
307                 pCurDocShell->SetChangeRecording( bDoRecordChanges );
308                 bModified = true;
309             }
310 
311             // change record protection
312             if (m_bNewPasswordIsValid &&
313                 bDoChangeProtection != pCurDocShell->HasChangeRecordProtection())
314             {
315                 DBG_ASSERT( !bDoChangeProtection || bDoRecordChanges,
316                         "change protection requires record changes to be active!" );
317                 pCurDocShell->SetProtectionPassword( m_aNewPassword );
318                 bModified = true;
319             }
320         }
321 
322         // open read-only?
323         const sal_Bool bDoOpenReadonly = m_aOpenReadonlyCB.IsChecked();
324         if (pCurDocShell->HasSecurityOptOpenReadOnly() &&
325             bDoOpenReadonly != pCurDocShell->IsSecurityOptOpenReadOnly())
326         {
327             pCurDocShell->SetSecurityOptOpenReadOnly( bDoOpenReadonly );
328             bModified = true;
329         }
330     }
331 
332     return bModified;
333 }
334 
335 
336 void SfxSecurityPage_Impl::Reset_Impl( const SfxItemSet & )
337 {
338     SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
339 
340     String sNewText = m_aProtectSTR;
341     if (!pCurDocShell)
342     {
343         // no doc -> hide document settings
344         m_aOpenReadonlyCB.Disable();
345         m_aRecordChangesCB.Disable();
346         m_aChangeProtectionPB.Disable();
347     }
348     else
349     {
350         bool bIsHTMLDoc = false;
351         SfxViewShell* pViewSh = SfxViewShell::Current();
352         if (pViewSh)
353         {
354             const SfxPoolItem* pItem;
355             SfxDispatcher* pDisp = pViewSh->GetDispatcher();
356             if (SFX_ITEM_AVAILABLE <= pDisp->QueryState( SID_HTML_MODE, pItem ))
357             {
358                 sal_uInt16 nMode = static_cast< const SfxUInt16Item* >( pItem )->GetValue();
359                 bIsHTMLDoc = ( ( nMode & HTMLMODE_ON ) != 0 );
360             }
361         }
362 
363         sal_Bool bIsReadonly = pCurDocShell->IsReadOnly();
364         if (pCurDocShell->HasSecurityOptOpenReadOnly() && !bIsHTMLDoc)
365         {
366             m_aOpenReadonlyCB.Check( pCurDocShell->IsSecurityOptOpenReadOnly() );
367             m_aOpenReadonlyCB.Enable( !bIsReadonly );
368         }
369         else
370             m_aOpenReadonlyCB.Disable();
371 
372         bool bRecordChanges;
373         if (QueryRecordChangesState( RL_WRITER, bRecordChanges ) && !bIsHTMLDoc)
374             m_eRedlingMode = RL_WRITER;
375         else if (QueryRecordChangesState( RL_CALC, bRecordChanges ))
376             m_eRedlingMode = RL_CALC;
377         else
378             m_eRedlingMode = RL_NONE;
379 
380         if (m_eRedlingMode != RL_NONE)
381         {
382             bool bProtection;
383             QueryRecordChangesProtectionState( m_eRedlingMode, bProtection );
384 
385             m_aChangeProtectionPB.Enable( !bIsReadonly );
386             // set the right text
387             if (bProtection)
388                 sNewText = m_aUnProtectSTR;
389 
390             m_aRecordChangesCB.Check( bRecordChanges );
391             m_aRecordChangesCB.Enable( /*!bProtection && */!bIsReadonly );
392 
393             m_bOrigPasswordIsConfirmed = true;   // default case if no password is set
394             uno::Sequence< sal_Int8 > aPasswordHash;
395             // check if password is available
396             if (pCurDocShell->GetProtectionHash( aPasswordHash ) &&
397                 aPasswordHash.getLength() > 0)
398                 m_bOrigPasswordIsConfirmed = false;  // password found, needs to be confirmed later on
399         }
400         else
401         {
402             // A Calc document that is shared will have 'm_eRedlingMode == RL_NONE'
403             // In shared documents change recording and protection must be disabled,
404             // similar to documents that do not support change recording at all.
405             m_aRecordChangesCB.Check( sal_False );
406             m_aRecordChangesCB.Disable();
407             m_aChangeProtectionPB.Check( sal_False );
408             m_aChangeProtectionPB.Disable();
409         }
410     }
411 
412     m_aChangeProtectionPB.SetText( sNewText );
413 }
414 
415 
416 IMPL_LINK( SfxSecurityPage_Impl, RecordChangesCBToggleHdl, void*, EMPTYARG )
417 {
418     // when change recording gets disabled protection must be disabled as well
419     if (!m_aRecordChangesCB.IsChecked())    // the new check state is already present, thus the '!'
420     {
421         bool bAlreadyDone = false;
422         if (!m_bEndRedliningWarningDone)
423         {
424 		    WarningBox aBox( m_rMyTabPage.GetParent(), WinBits(WB_YES_NO | WB_DEF_NO),
425                     m_aEndRedliningWarning );
426             if (aBox.Execute() != RET_YES)
427                 bAlreadyDone = true;
428             else
429                 m_bEndRedliningWarningDone = true;
430         }
431 
432         const bool bNeedPasssword = !m_bOrigPasswordIsConfirmed
433                 && m_aChangeProtectionPB.GetText() != m_aProtectSTR;
434         if (!bAlreadyDone && bNeedPasssword)
435         {
436             String aPasswordText;
437 
438             // dialog canceled or no password provided
439             if (!lcl_GetPassword( m_rMyTabPage.GetParent(), false, aPasswordText ))
440                 bAlreadyDone = true;
441 
442             // ask for password and if dialog is canceled or no password provided return
443             if (lcl_IsPasswordCorrect( aPasswordText ))
444                 m_bOrigPasswordIsConfirmed = true;
445             else
446                 bAlreadyDone = true;
447         }
448 
449         if (bAlreadyDone)
450             m_aRecordChangesCB.Check( true );     // restore original state
451         else
452         {
453             // remember required values to change protection and change recording in
454             // FillItemSet_Impl later on if password was correct.
455             m_bNewPasswordIsValid = true;
456             m_aNewPassword = String();
457 
458             m_aChangeProtectionPB.SetText( m_aProtectSTR );
459         }
460     }
461 
462     return 0;
463 }
464 
465 
466 IMPL_LINK( SfxSecurityPage_Impl, ChangeProtectionPBHdl, void*, EMPTYARG )
467 {
468     if (m_eRedlingMode == RL_NONE)
469         return 0;
470 
471     // the push button text is always the opposite of the current state. Thus:
472     const bool bCurrentProtection = m_aChangeProtectionPB.GetText() != m_aProtectSTR;
473 
474     // ask user for password (if still necessary)
475     String aPasswordText;
476     bool bNewProtection = !bCurrentProtection;
477     const bool bNeedPassword = bNewProtection || !m_bOrigPasswordIsConfirmed;
478     if (bNeedPassword)
479     {
480         // ask for password and if dialog is canceled or no password provided return
481         if (!lcl_GetPassword( m_rMyTabPage.GetParent(), bNewProtection, aPasswordText ))
482             return 0;
483 
484         // provided password still needs to be checked?
485         if (!bNewProtection && !m_bOrigPasswordIsConfirmed)
486         {
487             if (lcl_IsPasswordCorrect( aPasswordText ))
488                 m_bOrigPasswordIsConfirmed = true;
489             else
490                 return 0;
491         }
492     }
493     DBG_ASSERT( m_bOrigPasswordIsConfirmed, "ooops... this should not have happened!" );
494 
495     // remember required values to change protection and change recording in
496     // FillItemSet_Impl later on if password was correct.
497     m_bNewPasswordIsValid = true;
498     m_aNewPassword = bNewProtection? aPasswordText : String();
499 
500 //    // RecordChangesCB is enabled if protection is off
501 //    m_aRecordChangesCB.Enable( !bNewProtection );
502     m_aRecordChangesCB.Check( bNewProtection );
503     // toggle text of button "Protect" <-> "Unprotect"
504     m_aChangeProtectionPB.SetText( bNewProtection ? m_aUnProtectSTR : m_aProtectSTR );
505 
506     return 0;
507 }
508 
509 
510 //////////////////////////////////////////////////////////////////////
511 
512 
513 SfxTabPage* SfxSecurityPage::Create( Window * pParent, const SfxItemSet & rItemSet )
514 {
515     return new SfxSecurityPage( pParent, rItemSet );
516 }
517 
518 
519 SfxSecurityPage::SfxSecurityPage( Window* pParent, const SfxItemSet& rItemSet ) :
520     SfxTabPage( pParent, SfxResId( TP_DOCINFOSECURITY ), rItemSet )
521 {
522     m_pImpl = std::auto_ptr< SfxSecurityPage_Impl >(new SfxSecurityPage_Impl( *this, rItemSet ));
523 
524     FreeResource();
525 }
526 
527 
528 SfxSecurityPage::~SfxSecurityPage()
529 {
530 }
531 
532 
533 sal_Bool SfxSecurityPage::FillItemSet( SfxItemSet & rItemSet )
534 {
535     bool bModified = false;
536     DBG_ASSERT( m_pImpl.get(), "implementation pointer is 0. Still in c-tor?" );
537     if (m_pImpl.get() != 0)
538         bModified =  m_pImpl->FillItemSet_Impl( rItemSet );
539     return bModified;
540 }
541 
542 
543 void SfxSecurityPage::Reset( const SfxItemSet & rItemSet )
544 {
545     DBG_ASSERT( m_pImpl.get(), "implementation pointer is 0. Still in c-tor?" );
546     if (m_pImpl.get() != 0)
547         m_pImpl->Reset_Impl( rItemSet );
548 }
549 
550 
551 //////////////////////////////////////////////////////////////////////
552 
553 
554