/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "sfx2/securitypage.hxx" #include "securitypage.hrc" #include "sfxresid.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; namespace { enum RedliningMode { RL_NONE, RL_WRITER, RL_CALC }; enum RedlineFunc { RF_ON, RF_PROTECT }; bool QueryState( sal_uInt16 _nSlot, bool& _rValue ) { bool bRet = false; SfxViewShell* pViewSh = SfxViewShell::Current(); if (pViewSh) { const SfxPoolItem* pItem; SfxDispatcher* pDisp = pViewSh->GetDispatcher(); SfxItemState nState = pDisp->QueryState( _nSlot, pItem ); bRet = SFX_ITEM_AVAILABLE <= nState; if (bRet) _rValue = ( static_cast< const SfxBoolItem* >( pItem ) )->GetValue(); } return bRet; } bool QueryRecordChangesProtectionState( RedliningMode _eMode, bool& _rValue ) { bool bRet = false; if (_eMode != RL_NONE) { sal_uInt16 nSlot = _eMode == RL_WRITER ? FN_REDLINE_PROTECT : SID_CHG_PROTECT; bRet = QueryState( nSlot, _rValue ); } return bRet; } bool QueryRecordChangesState( RedliningMode _eMode, bool& _rValue ) { bool bRet = false; if (_eMode != RL_NONE) { sal_uInt16 nSlot = _eMode == RL_WRITER ? FN_REDLINE_ON : FID_CHG_RECORD; bRet = QueryState( nSlot, _rValue ); } return bRet; } } static short lcl_GetPassword( Window *pParent, bool bProtect, /*out*/String &rPassword ) { bool bRes = false; SfxPasswordDialog aPasswdDlg( pParent ); aPasswdDlg.SetMinLen( 1 ); if (bProtect) aPasswdDlg.ShowExtras( SHOWEXTRAS_CONFIRM ); if (RET_OK == aPasswdDlg.Execute() && aPasswdDlg.GetPassword().Len() > 0) { rPassword = aPasswdDlg.GetPassword(); bRes = true; } return bRes; } static bool lcl_IsPasswordCorrect( const String &rPassword ) { bool bRes = false; SfxObjectShell* pCurDocShell = SfxObjectShell::Current(); uno::Sequence< sal_Int8 > aPasswordHash; pCurDocShell->GetProtectionHash( aPasswordHash ); // check if supplied password was correct uno::Sequence< sal_Int8 > aNewPasswd( aPasswordHash ); SvPasswordHelper::GetHashPassword( aNewPasswd, rPassword ); if (SvPasswordHelper::CompareHashPassword( aPasswordHash, rPassword )) bRes = true; // password was correct else InfoBox( NULL, String( SfxResId( RID_SFX_INCORRECT_PASSWORD ) ) ).Execute(); return bRes; } struct SfxSecurityPage_Impl { SfxSecurityPage & m_rMyTabPage; FixedLine m_aNewPasswordToOpenFL; FixedText m_aNewPasswordToOpenFT; Edit m_aNewPasswordToOpenED; FixedText m_aConfirmPasswordToOpenFT; Edit m_aConfirmPasswordToOpenED; FixedText m_aNewPasswordInfoFT; FixedLine m_aNewPasswordToModifyFL; FixedText m_aNewPasswordToModifyFT; Edit m_aNewPasswordToModifyED; FixedText m_aConfirmPasswordToModifyFT; Edit m_aConfirmPasswordToModifyED; FixedLine m_aOptionsFL; CheckBox m_aOpenReadonlyCB; CheckBox m_aRecordChangesCB; // for record changes PushButton m_aChangeProtectionPB; // for record changes String m_aProtectSTR; // for record changes String m_aUnProtectSTR; // for record changes RedliningMode m_eRedlingMode; // for record changes bool m_bOrigPasswordIsConfirmed; bool m_bNewPasswordIsValid; String m_aNewPassword; String m_aEndRedliningWarning; bool m_bEndRedliningWarningDone; DECL_LINK( RecordChangesCBToggleHdl, void* ); DECL_LINK( ChangeProtectionPBHdl, void* ); SfxSecurityPage_Impl( SfxSecurityPage &rDlg, const SfxItemSet &rItemSet ); ~SfxSecurityPage_Impl(); sal_Bool FillItemSet_Impl( SfxItemSet & ); void Reset_Impl( const SfxItemSet & ); }; SfxSecurityPage_Impl::SfxSecurityPage_Impl( SfxSecurityPage &rTabPage, const SfxItemSet & ) : m_rMyTabPage (rTabPage), m_aNewPasswordToOpenFL (&rTabPage, SfxResId( PASSWORD_TO_OPEN_FL ) ), m_aNewPasswordToOpenFT (&rTabPage, SfxResId( PASSWORD_TO_OPEN_FT ) ), m_aNewPasswordToOpenED (&rTabPage, SfxResId( PASSWORD_TO_OPEN_ED ) ), m_aConfirmPasswordToOpenFT (&rTabPage, SfxResId( CONFIRM_PASSWORD_TO_OPEN_FT ) ), m_aConfirmPasswordToOpenED (&rTabPage, SfxResId( CONFIRM_PASSWORD_TO_OPEN_ED ) ), m_aNewPasswordInfoFT (&rTabPage, SfxResId( PASSWORD_INFO_FT ) ), m_aNewPasswordToModifyFL (&rTabPage, SfxResId( PASSWORD_TO_MODIFY_FL ) ), m_aNewPasswordToModifyFT (&rTabPage, SfxResId( PASSWORD_TO_MODIFY_FT ) ), m_aNewPasswordToModifyED (&rTabPage, SfxResId( PASSWORD_TO_MODIFY_ED ) ), m_aConfirmPasswordToModifyFT (&rTabPage, SfxResId( CONFIRM_PASSWORD_TO_MODIFY_FT ) ), m_aConfirmPasswordToModifyED (&rTabPage, SfxResId( CONFIRM_PASSWORD_TO_MODIFY_ED ) ), m_aOptionsFL (&rTabPage, SfxResId( OPTIONS_FL ) ), m_aOpenReadonlyCB (&rTabPage, SfxResId( OPEN_READONLY_CB ) ), m_aRecordChangesCB (&rTabPage, SfxResId( RECORD_CHANGES_CB ) ), m_aChangeProtectionPB (&rTabPage, SfxResId( CHANGE_PROTECTION_PB ) ), m_aProtectSTR ( SfxResId( STR_PROTECT ) ), m_aUnProtectSTR ( SfxResId( STR_UNPROTECT ) ), m_eRedlingMode ( RL_NONE ), m_bOrigPasswordIsConfirmed ( false ), m_bNewPasswordIsValid ( false ), m_aEndRedliningWarning ( SfxResId( STR_END_REDLINING_WARNING ) ), m_bEndRedliningWarningDone ( false ) { m_aChangeProtectionPB.SetText( m_aProtectSTR ); // adjust button width if necessary long nBtnTextWidth = 0; long nTemp = m_aChangeProtectionPB.GetCtrlTextWidth( m_aChangeProtectionPB.GetText() ); if (nTemp > nBtnTextWidth) nBtnTextWidth = nTemp; // force toggle hdl called before visual change of checkbox m_aRecordChangesCB.SetStyle( m_aRecordChangesCB.GetStyle() | WB_EARLYTOGGLE ); m_aRecordChangesCB.SetToggleHdl( LINK( this, SfxSecurityPage_Impl, RecordChangesCBToggleHdl ) ); m_aChangeProtectionPB.SetClickHdl( LINK( this, SfxSecurityPage_Impl, ChangeProtectionPBHdl ) ); // #i112277: for the time being (OOO 3.3) the following options should not // be available. In the long run however it is planned to implement the yet // missing functionality. Thus now we hide them and move the remaining ones up. m_aNewPasswordToOpenFL.Hide(); m_aNewPasswordToOpenFT.Hide(); m_aNewPasswordToOpenED.Hide(); m_aConfirmPasswordToOpenFT.Hide(); m_aConfirmPasswordToOpenED.Hide(); m_aNewPasswordInfoFT.Hide(); m_aNewPasswordToModifyFL.Hide(); m_aNewPasswordToModifyFT.Hide(); m_aNewPasswordToModifyED.Hide(); m_aConfirmPasswordToModifyFT.Hide(); m_aConfirmPasswordToModifyED.Hide(); const long nDelta = m_aOptionsFL.GetPosPixel().Y() - m_aNewPasswordToOpenFL.GetPosPixel().Y(); Point aPos; aPos = m_aOptionsFL.GetPosPixel(); aPos.Y() -= nDelta; m_aOptionsFL.SetPosPixel( aPos ); aPos = m_aOpenReadonlyCB.GetPosPixel(); aPos.Y() -= nDelta; m_aOpenReadonlyCB.SetPosPixel( aPos ); aPos = m_aRecordChangesCB.GetPosPixel(); aPos.Y() -= nDelta; m_aRecordChangesCB.SetPosPixel( aPos ); aPos = m_aChangeProtectionPB.GetPosPixel(); aPos.Y() -= nDelta; m_aChangeProtectionPB.SetPosPixel( aPos ); } SfxSecurityPage_Impl::~SfxSecurityPage_Impl() { } sal_Bool SfxSecurityPage_Impl::FillItemSet_Impl( SfxItemSet & ) { bool bModified = false; SfxObjectShell* pCurDocShell = SfxObjectShell::Current(); if (pCurDocShell&& !pCurDocShell->IsReadOnly()) { if (m_eRedlingMode != RL_NONE ) { const bool bDoRecordChanges = m_aRecordChangesCB.IsChecked(); const bool bDoChangeProtection = m_aChangeProtectionPB.GetText() != m_aProtectSTR; // sanity checks DBG_ASSERT( bDoRecordChanges || !bDoChangeProtection, "no change recording should imply no change protection" ); DBG_ASSERT( bDoChangeProtection || !bDoRecordChanges, "no change protection should imply no change recording" ); DBG_ASSERT( !bDoChangeProtection || m_aNewPassword.Len() > 0, "change protection should imply password length is > 0" ); DBG_ASSERT( bDoChangeProtection || m_aNewPassword.Len() == 0, "no change protection should imply password length is 0" ); // change recording if (bDoRecordChanges != pCurDocShell->IsChangeRecording()) { pCurDocShell->SetChangeRecording( bDoRecordChanges ); bModified = true; } // change record protection if (m_bNewPasswordIsValid && bDoChangeProtection != pCurDocShell->HasChangeRecordProtection()) { DBG_ASSERT( !bDoChangeProtection || bDoRecordChanges, "change protection requires record changes to be active!" ); pCurDocShell->SetProtectionPassword( m_aNewPassword ); bModified = true; } } // open read-only? const sal_Bool bDoOpenReadonly = m_aOpenReadonlyCB.IsChecked(); if (pCurDocShell->HasSecurityOptOpenReadOnly() && bDoOpenReadonly != pCurDocShell->IsSecurityOptOpenReadOnly()) { pCurDocShell->SetSecurityOptOpenReadOnly( bDoOpenReadonly ); bModified = true; } } return bModified; } void SfxSecurityPage_Impl::Reset_Impl( const SfxItemSet & ) { SfxObjectShell* pCurDocShell = SfxObjectShell::Current(); String sNewText = m_aProtectSTR; if (!pCurDocShell) { // no doc -> hide document settings m_aOpenReadonlyCB.Disable(); m_aRecordChangesCB.Disable(); m_aChangeProtectionPB.Disable(); } else { bool bIsHTMLDoc = false; SfxViewShell* pViewSh = SfxViewShell::Current(); if (pViewSh) { const SfxPoolItem* pItem; SfxDispatcher* pDisp = pViewSh->GetDispatcher(); if (SFX_ITEM_AVAILABLE <= pDisp->QueryState( SID_HTML_MODE, pItem )) { sal_uInt16 nMode = static_cast< const SfxUInt16Item* >( pItem )->GetValue(); bIsHTMLDoc = ( ( nMode & HTMLMODE_ON ) != 0 ); } } sal_Bool bIsReadonly = pCurDocShell->IsReadOnly(); if (pCurDocShell->HasSecurityOptOpenReadOnly() && !bIsHTMLDoc) { m_aOpenReadonlyCB.Check( pCurDocShell->IsSecurityOptOpenReadOnly() ); m_aOpenReadonlyCB.Enable( !bIsReadonly ); } else m_aOpenReadonlyCB.Disable(); bool bRecordChanges; if (QueryRecordChangesState( RL_WRITER, bRecordChanges ) && !bIsHTMLDoc) m_eRedlingMode = RL_WRITER; else if (QueryRecordChangesState( RL_CALC, bRecordChanges )) m_eRedlingMode = RL_CALC; else m_eRedlingMode = RL_NONE; if (m_eRedlingMode != RL_NONE) { bool bProtection; QueryRecordChangesProtectionState( m_eRedlingMode, bProtection ); m_aChangeProtectionPB.Enable( !bIsReadonly ); // set the right text if (bProtection) sNewText = m_aUnProtectSTR; m_aRecordChangesCB.Check( bRecordChanges ); m_aRecordChangesCB.Enable( /*!bProtection && */!bIsReadonly ); m_bOrigPasswordIsConfirmed = true; // default case if no password is set uno::Sequence< sal_Int8 > aPasswordHash; // check if password is available if (pCurDocShell->GetProtectionHash( aPasswordHash ) && aPasswordHash.getLength() > 0) m_bOrigPasswordIsConfirmed = false; // password found, needs to be confirmed later on } else { // A Calc document that is shared will have 'm_eRedlingMode == RL_NONE' // In shared documents change recording and protection must be disabled, // similar to documents that do not support change recording at all. m_aRecordChangesCB.Check( sal_False ); m_aRecordChangesCB.Disable(); m_aChangeProtectionPB.Check( sal_False ); m_aChangeProtectionPB.Disable(); } } m_aChangeProtectionPB.SetText( sNewText ); } IMPL_LINK_NOARG(SfxSecurityPage_Impl, RecordChangesCBToggleHdl) { // when change recording gets disabled protection must be disabled as well if (!m_aRecordChangesCB.IsChecked()) // the new check state is already present, thus the '!' { bool bAlreadyDone = false; if (!m_bEndRedliningWarningDone) { WarningBox aBox( m_rMyTabPage.GetParent(), WinBits(WB_YES_NO | WB_DEF_NO), m_aEndRedliningWarning ); if (aBox.Execute() != RET_YES) bAlreadyDone = true; else m_bEndRedliningWarningDone = true; } const bool bNeedPasssword = !m_bOrigPasswordIsConfirmed && m_aChangeProtectionPB.GetText() != m_aProtectSTR; if (!bAlreadyDone && bNeedPasssword) { String aPasswordText; // dialog canceled or no password provided if (!lcl_GetPassword( m_rMyTabPage.GetParent(), false, aPasswordText )) bAlreadyDone = true; // ask for password and if dialog is canceled or no password provided return if (lcl_IsPasswordCorrect( aPasswordText )) m_bOrigPasswordIsConfirmed = true; else bAlreadyDone = true; } if (bAlreadyDone) m_aRecordChangesCB.Check( true ); // restore original state else { // remember required values to change protection and change recording in // FillItemSet_Impl later on if password was correct. m_bNewPasswordIsValid = true; m_aNewPassword = String(); m_aChangeProtectionPB.SetText( m_aProtectSTR ); } } return 0; } IMPL_LINK_NOARG(SfxSecurityPage_Impl, ChangeProtectionPBHdl) { if (m_eRedlingMode == RL_NONE) return 0; // the push button text is always the opposite of the current state. Thus: const bool bCurrentProtection = m_aChangeProtectionPB.GetText() != m_aProtectSTR; // ask user for password (if still necessary) String aPasswordText; bool bNewProtection = !bCurrentProtection; const bool bNeedPassword = bNewProtection || !m_bOrigPasswordIsConfirmed; if (bNeedPassword) { // ask for password and if dialog is canceled or no password provided return if (!lcl_GetPassword( m_rMyTabPage.GetParent(), bNewProtection, aPasswordText )) return 0; // provided password still needs to be checked? if (!bNewProtection && !m_bOrigPasswordIsConfirmed) { if (lcl_IsPasswordCorrect( aPasswordText )) m_bOrigPasswordIsConfirmed = true; else return 0; } } DBG_ASSERT( m_bOrigPasswordIsConfirmed, "ooops... this should not have happened!" ); // remember required values to change protection and change recording in // FillItemSet_Impl later on if password was correct. m_bNewPasswordIsValid = true; m_aNewPassword = bNewProtection? aPasswordText : String(); m_aRecordChangesCB.Check( bNewProtection ); // toggle text of button "Protect" <-> "Unprotect" m_aChangeProtectionPB.SetText( bNewProtection ? m_aUnProtectSTR : m_aProtectSTR ); return 0; } SfxTabPage* SfxSecurityPage::Create( Window * pParent, const SfxItemSet & rItemSet ) { return new SfxSecurityPage( pParent, rItemSet ); } SfxSecurityPage::SfxSecurityPage( Window* pParent, const SfxItemSet& rItemSet ) : SfxTabPage( pParent, SfxResId( TP_DOCINFOSECURITY ), rItemSet ) { m_pImpl = std::auto_ptr< SfxSecurityPage_Impl >(new SfxSecurityPage_Impl( *this, rItemSet )); FreeResource(); } SfxSecurityPage::~SfxSecurityPage() { } sal_Bool SfxSecurityPage::FillItemSet( SfxItemSet & rItemSet ) { bool bModified = false; DBG_ASSERT( m_pImpl.get(), "implementation pointer is 0. Still in c-tor?" ); if (m_pImpl.get() != 0) bModified = m_pImpl->FillItemSet_Impl( rItemSet ); return bModified; } void SfxSecurityPage::Reset( const SfxItemSet & rItemSet ) { DBG_ASSERT( m_pImpl.get(), "implementation pointer is 0. Still in c-tor?" ); if (m_pImpl.get() != 0) m_pImpl->Reset_Impl( rItemSet ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */