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