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 "tabprotection.hxx"
21 #include <svl/PasswordHelper.hxx>
22 #include <comphelper/docpasswordhelper.hxx>
23 #include <osl/diagnose.h>
24 #include "document.hxx"
28 #define DEBUG_TAB_PROTECTION 0
30 #define URI_SHA1 "http://www.w3.org/2000/09/xmldsig#sha1"
31 #define URI_XLS_LEGACY "http://docs.oasis-open.org/office/ns/table/legacy-hash-excel"
33 using namespace ::com::sun::star
;
34 using ::com::sun::star::uno::Sequence
;
37 bool ScPassHashHelper::needsPassHashRegen(const ScDocument
& rDoc
, ScPasswordHash eHash1
, ScPasswordHash eHash2
)
39 if (rDoc
.IsDocProtected())
41 const ScDocProtection
* p
= rDoc
.GetDocProtection();
42 if (!p
->isPasswordEmpty() && !p
->hasPasswordHash(eHash1
, eHash2
))
46 SCTAB nTabCount
= rDoc
.GetTableCount();
47 for (SCTAB i
= 0; i
< nTabCount
; ++i
)
49 const ScTableProtection
* p
= rDoc
.GetTabProtection(i
);
50 if (!p
|| !p
->isProtected())
51 // Sheet not protected. Skip it.
54 if (!p
->isPasswordEmpty() && !p
->hasPasswordHash(eHash1
, eHash2
))
61 OUString
ScPassHashHelper::getHashURI(ScPasswordHash eHash
)
66 return OUString(URI_SHA1
);
68 return OUString(URI_XLS_LEGACY
);
69 case PASSHASH_UNSPECIFIED
:
76 ScPasswordHash
ScPassHashHelper::getHashTypeFromURI(const OUString
& rURI
)
78 if ( rURI
== URI_SHA1
)
80 else if ( rURI
== URI_XLS_LEGACY
)
82 return PASSHASH_UNSPECIFIED
;
85 ScPassHashProtectable::~ScPassHashProtectable()
89 class ScTableProtectionImpl
92 static Sequence
<sal_Int8
> hashPassword(const OUString
& aPassText
, ScPasswordHash eHash
= PASSHASH_SHA1
);
93 static Sequence
<sal_Int8
> hashPassword(const Sequence
<sal_Int8
>& rPassHash
, ScPasswordHash eHash
= PASSHASH_SHA1
);
95 explicit ScTableProtectionImpl(SCSIZE nOptSize
);
96 explicit ScTableProtectionImpl(const ScTableProtectionImpl
& r
);
98 bool isProtected() const { return mbProtected
;}
99 bool isProtectedWithPass() const;
100 void setProtected(bool bProtected
);
102 bool isPasswordEmpty() const { return mbEmptyPass
;}
103 bool hasPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
= PASSHASH_UNSPECIFIED
) const;
104 void setPassword(const OUString
& aPassText
);
105 ::com::sun::star::uno::Sequence
<sal_Int8
> getPasswordHash(
106 ScPasswordHash eHash
, ScPasswordHash eHash2
= PASSHASH_UNSPECIFIED
) const;
107 void setPasswordHash(
108 const ::com::sun::star::uno::Sequence
<sal_Int8
>& aPassword
,
109 ScPasswordHash eHash
= PASSHASH_SHA1
, ScPasswordHash eHash2
= PASSHASH_UNSPECIFIED
);
110 bool verifyPassword(const OUString
& aPassText
) const;
112 bool isOptionEnabled(SCSIZE nOptId
) const;
113 void setOption(SCSIZE nOptId
, bool bEnabled
);
115 void setEnhancedProtection( const ::std::vector
< ScEnhancedProtection
> & rProt
);
116 const ::std::vector
< ScEnhancedProtection
> & getEnhancedProtection() const { return maEnhancedProtection
;}
117 bool updateReference( UpdateRefMode
, ScDocument
*, const ScRange
& rWhere
, SCsCOL nDx
, SCsROW nDy
, SCsTAB nDz
);
118 bool isBlockEditable( const ScRange
& rRange
) const;
119 bool isSelectionEditable( const ScRangeList
& rRangeList
) const;
123 ::com::sun::star::uno::Sequence
<sal_Int8
> maPassHash
;
124 ::std::vector
<bool> maOptions
;
127 ScPasswordHash meHash1
;
128 ScPasswordHash meHash2
;
129 ::std::vector
< ScEnhancedProtection
> maEnhancedProtection
;
132 Sequence
<sal_Int8
> ScTableProtectionImpl::hashPassword(const OUString
& aPassText
, ScPasswordHash eHash
)
134 Sequence
<sal_Int8
> aHash
;
138 aHash
= ::comphelper::DocPasswordHelper::GetXLHashAsSequence( aPassText
, RTL_TEXTENCODING_UTF8
);
141 SvPasswordHelper::GetHashPassword(aHash
, aPassText
);
149 Sequence
<sal_Int8
> ScTableProtectionImpl::hashPassword(
150 const Sequence
<sal_Int8
>& rPassHash
, ScPasswordHash eHash
)
152 if (!rPassHash
.getLength() || eHash
== PASSHASH_UNSPECIFIED
)
155 // TODO: Right now, we only support double-hash by SHA1.
156 if (eHash
== PASSHASH_SHA1
)
158 vector
<sal_Char
> aChars
;
159 sal_Int32 n
= rPassHash
.getLength();
161 for (sal_Int32 i
= 0; i
< n
; ++i
)
162 aChars
.push_back(static_cast<sal_Char
>(rPassHash
[i
]));
164 Sequence
<sal_Int8
> aNewHash
;
165 SvPasswordHelper::GetHashPassword(aNewHash
, &aChars
[0], aChars
.size());
172 ScTableProtectionImpl::ScTableProtectionImpl(SCSIZE nOptSize
) :
176 meHash1(PASSHASH_SHA1
),
177 meHash2(PASSHASH_UNSPECIFIED
)
181 ScTableProtectionImpl::ScTableProtectionImpl(const ScTableProtectionImpl
& r
) :
182 maPassText(r
.maPassText
),
183 maPassHash(r
.maPassHash
),
184 maOptions(r
.maOptions
),
185 mbEmptyPass(r
.mbEmptyPass
),
186 mbProtected(r
.mbProtected
),
189 maEnhancedProtection(r
.maEnhancedProtection
)
193 bool ScTableProtectionImpl::isProtectedWithPass() const
198 return !maPassText
.isEmpty() || maPassHash
.getLength();
201 void ScTableProtectionImpl::setProtected(bool bProtected
)
203 mbProtected
= bProtected
;
204 // We need to keep the old password even when the protection is off. So,
205 // don't erase the password data here.
208 void ScTableProtectionImpl::setPassword(const OUString
& aPassText
)
210 // We can't hash it here because we don't know whether this document will
211 // get saved to Excel or ODF, depending on which we will need to use a
212 // different hashing algorithm. One alternative is to hash it using all
213 // hash algorithms that we support, and store them all.
215 maPassText
= aPassText
;
216 mbEmptyPass
= aPassText
.isEmpty();
219 maPassHash
= Sequence
<sal_Int8
>();
223 bool ScTableProtectionImpl::hasPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
228 if (!maPassText
.isEmpty())
231 if (meHash1
== eHash
)
233 if (meHash2
== PASSHASH_UNSPECIFIED
)
237 return meHash2
== eHash2
;
243 Sequence
<sal_Int8
> ScTableProtectionImpl::getPasswordHash(
244 ScPasswordHash eHash
, ScPasswordHash eHash2
) const
246 Sequence
<sal_Int8
> aPassHash
;
252 if (!maPassText
.isEmpty())
254 // Cleartext password exists. Hash it.
255 aPassHash
= hashPassword(maPassText
, eHash
);
256 if (eHash2
!= PASSHASH_UNSPECIFIED
)
258 aPassHash
= hashPassword(aPassHash
, eHash2
);
264 // No clear text password. Check if we have a hash value of the right hash type.
265 if (meHash1
== eHash
)
267 aPassHash
= maPassHash
;
269 if (meHash2
== eHash2
)
270 // Matching double-hash requested.
272 else if (meHash2
== PASSHASH_UNSPECIFIED
)
273 // primary hashing type match. Double hash it by the requested
275 return hashPassword(aPassHash
, eHash2
);
280 return Sequence
<sal_Int8
>();
283 void ScTableProtectionImpl::setPasswordHash(
284 const uno::Sequence
<sal_Int8
>& aPassword
, ScPasswordHash eHash
, ScPasswordHash eHash2
)
286 sal_Int32 nLen
= aPassword
.getLength();
287 mbEmptyPass
= nLen
<= 0;
290 maPassHash
= aPassword
;
292 #if DEBUG_TAB_PROTECTION
293 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
294 printf("%2.2X ", static_cast<sal_uInt8
>(aPassword
[i
]));
299 bool ScTableProtectionImpl::verifyPassword(const OUString
& aPassText
) const
301 #if DEBUG_TAB_PROTECTION
302 fprintf(stdout
, "ScTableProtectionImpl::verifyPassword: input = '%s'\n",
303 OUStringToOString(OUString(aPassText
), RTL_TEXTENCODING_UTF8
).getStr());
307 return aPassText
.isEmpty();
309 if (!maPassText
.isEmpty())
310 // Clear text password exists, and this one takes precedence.
311 return aPassText
== maPassText
;
313 Sequence
<sal_Int8
> aHash
= hashPassword(aPassText
, meHash1
);
314 aHash
= hashPassword(aHash
, meHash2
);
316 #if DEBUG_TAB_PROTECTION
317 fprintf(stdout
, "ScTableProtectionImpl::verifyPassword: hash = ");
318 for (sal_Int32 i
= 0; i
< aHash
.getLength(); ++i
)
319 printf("%2.2X ", static_cast<sal_uInt8
>(aHash
[i
]));
323 return aHash
== maPassHash
;
326 bool ScTableProtectionImpl::isOptionEnabled(SCSIZE nOptId
) const
328 if ( maOptions
.size() <= static_cast<size_t>(nOptId
) )
330 OSL_FAIL("ScTableProtectionImpl::isOptionEnabled: wrong size");
334 return maOptions
[nOptId
];
337 void ScTableProtectionImpl::setOption(SCSIZE nOptId
, bool bEnabled
)
339 if ( maOptions
.size() <= static_cast<size_t>(nOptId
) )
341 OSL_FAIL("ScTableProtectionImpl::setOption: wrong size");
345 maOptions
[nOptId
] = bEnabled
;
348 void ScTableProtectionImpl::setEnhancedProtection( const ::std::vector
< ScEnhancedProtection
> & rProt
)
350 maEnhancedProtection
= rProt
;
353 bool ScTableProtectionImpl::updateReference( UpdateRefMode eMode
, ScDocument
* pDoc
,
354 const ScRange
& rWhere
, SCsCOL nDx
, SCsROW nDy
, SCsTAB nDz
)
356 bool bChanged
= false;
357 for (::std::vector
<ScEnhancedProtection
>::iterator
it(maEnhancedProtection
.begin());
358 it
!= maEnhancedProtection
.end(); ++it
)
360 if ((*it
).maRangeList
.Is())
361 bChanged
|= (*it
).maRangeList
->UpdateReference( eMode
, pDoc
, rWhere
, nDx
, nDy
, nDz
);
366 bool ScTableProtectionImpl::isBlockEditable( const ScRange
& rRange
) const
368 /* TODO: ask for password (and remember) if a password was set for
369 * a matching range and no matching range without password was encountered.
370 * Would need another return type than boolean to reflect
371 * "password required for a specific protection". */
373 // No protection exception or overriding permission to edit if empty.
374 if (maEnhancedProtection
.empty())
377 // No security descriptor in an enhanced protection means the ranges of
378 // that protection are editable. If there is any security descriptor
379 // present we assume the permission to edit is not granted. Until we
380 // actually can evaluate the descriptors..
382 for (::std::vector
<ScEnhancedProtection
>::const_iterator
it(maEnhancedProtection
.begin()),
383 itEnd(maEnhancedProtection
.end()); it
!= itEnd
; ++it
)
385 if (!(*it
).hasSecurityDescriptor() && (*it
).maRangeList
.Is())
387 if ((*it
).maRangeList
->In( rRange
))
389 // Range is editable if no password is assigned.
390 if (!(*it
).hasPassword())
396 // For a single address, a simple check with single ranges was sufficient.
397 if (rRange
.aStart
== rRange
.aEnd
)
400 // Test also for cases where rRange is encompassed by a union of two or
401 // more ranges of the list. The original ranges are not necessarily joined.
402 for (::std::vector
<ScEnhancedProtection
>::const_iterator
it(maEnhancedProtection
.begin()),
403 itEnd(maEnhancedProtection
.end()); it
!= itEnd
; ++it
)
405 if (!(*it
).hasSecurityDescriptor() && (*it
).maRangeList
.Is())
407 ScRangeList
aList( (*it
).maRangeList
->GetIntersectedRange( rRange
));
408 if (aList
.size() == 1 && *aList
[0] == rRange
)
410 // Range is editable if no password is assigned.
411 if (!(*it
).hasPassword())
417 // Ranges may even be distributed over different protection records, for
418 // example if they are assigned different names, and can have different
419 // passwords. Combine the ones that can be edited.
420 /* TODO: once we handle passwords, remember a successful unlock at
421 * ScEnhancedProtection so we can use that here. */
422 ScRangeList aRangeList
;
423 for (::std::vector
<ScEnhancedProtection
>::const_iterator
it(maEnhancedProtection
.begin()),
424 itEnd(maEnhancedProtection
.end()); it
!= itEnd
; ++it
)
426 if (!(*it
).hasSecurityDescriptor() && (*it
).maRangeList
.Is())
428 // Ranges are editable if no password is assigned.
429 if (!(*it
).hasPassword())
431 const ScRangeList
& rRanges
= *(*it
).maRangeList
;
432 size_t nRanges
= rRanges
.size();
433 for (size_t i
=0; i
< nRanges
; ++i
)
435 aRangeList
.Append( *rRanges
[i
]);
440 ScRangeList
aResultList( aRangeList
.GetIntersectedRange( rRange
));
441 if (aResultList
.size() == 1 && *aResultList
[0] == rRange
)
447 bool ScTableProtectionImpl::isSelectionEditable( const ScRangeList
& rRangeList
) const
449 if (rRangeList
.empty())
452 for (size_t i
=0, nRanges
= rRangeList
.size(); i
< nRanges
; ++i
)
454 if (!isBlockEditable( *rRangeList
[i
]))
460 ScDocProtection::ScDocProtection() :
461 mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE
>(ScDocProtection::NONE
)))
465 ScDocProtection::ScDocProtection(const ScDocProtection
& r
) :
466 ScPassHashProtectable(),
467 mpImpl(new ScTableProtectionImpl(*r
.mpImpl
))
471 ScDocProtection::~ScDocProtection()
475 bool ScDocProtection::isProtected() const
477 return mpImpl
->isProtected();
480 bool ScDocProtection::isProtectedWithPass() const
482 return mpImpl
->isProtectedWithPass();
485 void ScDocProtection::setProtected(bool bProtected
)
487 mpImpl
->setProtected(bProtected
);
489 // Currently Calc doesn't support document protection options. So, let's
490 // assume that when the document is protected, its structure is protected.
491 // We need to do this for Excel export.
492 mpImpl
->setOption(ScDocProtection::STRUCTURE
, bProtected
);
495 bool ScDocProtection::isPasswordEmpty() const
497 return mpImpl
->isPasswordEmpty();
500 bool ScDocProtection::hasPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
502 return mpImpl
->hasPasswordHash(eHash
, eHash2
);
505 void ScDocProtection::setPassword(const OUString
& aPassText
)
507 mpImpl
->setPassword(aPassText
);
510 uno::Sequence
<sal_Int8
> ScDocProtection::getPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
512 return mpImpl
->getPasswordHash(eHash
, eHash2
);
515 void ScDocProtection::setPasswordHash(
516 const uno::Sequence
<sal_Int8
>& aPassword
, ScPasswordHash eHash
, ScPasswordHash eHash2
)
518 mpImpl
->setPasswordHash(aPassword
, eHash
, eHash2
);
521 bool ScDocProtection::verifyPassword(const OUString
& aPassText
) const
523 return mpImpl
->verifyPassword(aPassText
);
526 bool ScDocProtection::isOptionEnabled(Option eOption
) const
528 return mpImpl
->isOptionEnabled(eOption
);
531 void ScDocProtection::setOption(Option eOption
, bool bEnabled
)
533 mpImpl
->setOption(eOption
, bEnabled
);
536 ScTableProtection::ScTableProtection() :
537 mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE
>(ScTableProtection::NONE
)))
539 // Set default values for the options.
540 mpImpl
->setOption(SELECT_LOCKED_CELLS
, true);
541 mpImpl
->setOption(SELECT_UNLOCKED_CELLS
, true);
544 ScTableProtection::ScTableProtection(const ScTableProtection
& r
) :
545 ScPassHashProtectable(),
546 mpImpl(new ScTableProtectionImpl(*r
.mpImpl
))
550 ScTableProtection::~ScTableProtection()
554 bool ScTableProtection::isProtected() const
556 return mpImpl
->isProtected();
559 bool ScTableProtection::isProtectedWithPass() const
561 return mpImpl
->isProtectedWithPass();
564 void ScTableProtection::setProtected(bool bProtected
)
566 mpImpl
->setProtected(bProtected
);
569 bool ScTableProtection::isPasswordEmpty() const
571 return mpImpl
->isPasswordEmpty();
574 bool ScTableProtection::hasPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
576 return mpImpl
->hasPasswordHash(eHash
, eHash2
);
579 void ScTableProtection::setPassword(const OUString
& aPassText
)
581 mpImpl
->setPassword(aPassText
);
584 Sequence
<sal_Int8
> ScTableProtection::getPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
586 return mpImpl
->getPasswordHash(eHash
, eHash2
);
589 void ScTableProtection::setPasswordHash(
590 const uno::Sequence
<sal_Int8
>& aPassword
, ScPasswordHash eHash
, ScPasswordHash eHash2
)
592 mpImpl
->setPasswordHash(aPassword
, eHash
, eHash2
);
595 bool ScTableProtection::verifyPassword(const OUString
& aPassText
) const
597 return mpImpl
->verifyPassword(aPassText
);
600 bool ScTableProtection::isOptionEnabled(Option eOption
) const
602 return mpImpl
->isOptionEnabled(eOption
);
605 void ScTableProtection::setOption(Option eOption
, bool bEnabled
)
607 mpImpl
->setOption(eOption
, bEnabled
);
610 void ScTableProtection::setEnhancedProtection( const ::std::vector
< ScEnhancedProtection
> & rProt
)
612 mpImpl
->setEnhancedProtection(rProt
);
615 const ::std::vector
< ScEnhancedProtection
> & ScTableProtection::getEnhancedProtection() const
617 return mpImpl
->getEnhancedProtection();
620 bool ScTableProtection::updateReference( UpdateRefMode eMode
, ScDocument
* pDoc
, const ScRange
& rWhere
,
621 SCsCOL nDx
, SCsROW nDy
, SCsTAB nDz
)
623 return mpImpl
->updateReference( eMode
, pDoc
, rWhere
, nDx
, nDy
, nDz
);
626 bool ScTableProtection::isBlockEditable( const ScRange
& rRange
) const
628 return mpImpl
->isBlockEditable( rRange
);
631 bool ScTableProtection::isSelectionEditable( const ScRangeList
& rRangeList
) const
633 return mpImpl
->isSelectionEditable( rRangeList
);
636 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */