diff --git a/ClosedXML/Excel/XLWorkbook.cs b/ClosedXML/Excel/XLWorkbook.cs index ed6398f..9e86a67 100644 --- a/ClosedXML/Excel/XLWorkbook.cs +++ b/ClosedXML/Excel/XLWorkbook.cs @@ -1,14 +1,14 @@ +using ClosedXML.Excel.CalcEngine; +using ClosedXML.Extensions; +using DocumentFormat.OpenXml; using System; using System.Collections.Generic; +using System.Data; using System.IO; -using System.Security.AccessControl; -using ClosedXML.Excel.CalcEngine; -using DocumentFormat.OpenXml; +using System.Linq; namespace ClosedXML.Excel { - using System.Linq; - using System.Data; public enum XLEventTracking { Enabled, Disabled } public enum XLCalculateMode @@ -865,47 +865,34 @@ public Boolean LockWindows { get; set; } public XLWorkbook SetLockWindows(Boolean value) { LockWindows = value; return this; } internal HexBinaryValue LockPassword { get; set; } + public Boolean IsPasswordProtected { get { return LockPassword != null; } } public void Protect(Boolean lockStructure, Boolean lockWindows, String workbookPassword) { - if (workbookPassword != null) + if (IsPasswordProtected && workbookPassword == null) + throw new InvalidOperationException("The workbook is password protected"); + + var hashPassword = workbookPassword.HashPassword(); + if (IsPasswordProtected && LockPassword != hashPassword) + throw new ArgumentException("Invalid password"); + + if (IsPasswordProtected && (lockStructure || lockWindows)) + throw new InvalidOperationException("The workbook is already protected"); + + if (IsPasswordProtected && hashPassword != null && !lockStructure && !lockWindows) { - var hashPassword = GetPasswordHash(workbookPassword); - if (LockPassword != null) - { - if (LockPassword != hashPassword) - { - throw new ArgumentException("Invalid password"); - } - else - { - if (lockStructure || lockWindows) - { - throw new InvalidOperationException("The workbook is already protected"); - } - else - { - //Unprotect workbook using password. - LockPassword = null; - } - } - } - else - { - if (lockStructure || lockWindows) - { - //Protect workbook using password. - LockPassword = hashPassword; - } - } + // Workbook currently protected, but we're unsetting the 2 flags + // Hence unprotect workbook using password. + LockPassword = null; } - else + + + if (!IsPasswordProtected && hashPassword != null && (lockStructure || lockWindows)) { - if (LockPassword != null) - { - throw new InvalidOperationException("The workbook is password protected"); - } + //Protect workbook using password. + LockPassword = hashPassword; } + LockStructure = lockStructure; LockWindows = lockWindows; } @@ -939,21 +926,5 @@ { Protect(false, false, workbookPassword); } - - private String GetPasswordHash(String password) - { - Int32 pLength = password.Length; - Int32 hash = 0; - if (pLength == 0) return String.Empty; - - for (Int32 i = pLength - 1; i >= 0; i--) - { - hash ^= password[i]; - hash = hash >> 14 & 0x01 | hash << 1 & 0x7fff; - } - hash ^= 0x8000 | 'N' << 8 | 'K'; - hash ^= pLength; - return hash.ToString("X"); - } } } \ No newline at end of file diff --git a/ClosedXML/Extensions/StringExtensions.cs b/ClosedXML/Extensions/StringExtensions.cs index a3b49ec..facf0a5 100644 --- a/ClosedXML/Extensions/StringExtensions.cs +++ b/ClosedXML/Extensions/StringExtensions.cs @@ -8,12 +8,31 @@ { internal static class StringExtensions { - internal static string WrapSheetNameInQuotesIfRequired(this string sheetName) + internal static String WrapSheetNameInQuotesIfRequired(this String sheetName) { if (sheetName.Contains(' ')) return "'" + sheetName + "'"; else return sheetName; } + + internal static String HashPassword(this String password) + { + if (password == null) return null; + + Int32 pLength = password.Length; + Int32 hash = 0; + if (pLength == 0) return String.Empty; + + for (Int32 i = pLength - 1; i >= 0; i--) + { + hash ^= password[i]; + hash = hash >> 14 & 0x01 | hash << 1 & 0x7fff; + } + hash ^= 0x8000 | 'N' << 8 | 'K'; + hash ^= pLength; + return hash.ToString("X"); + } + } } diff --git a/ClosedXML_Tests/Excel/Misc/XLWorkbookTests.cs b/ClosedXML_Tests/Excel/Misc/XLWorkbookTests.cs index 55afe19..5a1e4a7 100644 --- a/ClosedXML_Tests/Excel/Misc/XLWorkbookTests.cs +++ b/ClosedXML_Tests/Excel/Misc/XLWorkbookTests.cs @@ -255,7 +255,7 @@ Assert.AreEqual("$A$1:$A$1", wsRanges.First().RangeAddress.ToStringFixed()); Assert.AreEqual("$A$3:$A$3", wsRanges.Last().RangeAddress.ToStringFixed()); } - + [Test] public void WbProtect1() { @@ -265,6 +265,7 @@ wb.Protect(); Assert.IsTrue(wb.LockStructure); Assert.IsFalse(wb.LockWindows); + Assert.IsFalse(wb.IsPasswordProtected); } } @@ -277,6 +278,7 @@ wb.Protect(true, false); Assert.IsTrue(wb.LockStructure); Assert.IsFalse(wb.LockWindows); + Assert.IsFalse(wb.IsPasswordProtected); } } @@ -289,6 +291,7 @@ wb.Protect("Abc@123"); Assert.IsTrue(wb.LockStructure); Assert.IsFalse(wb.LockWindows); + Assert.IsTrue(wb.IsPasswordProtected); Assert.Throws(typeof(InvalidOperationException), delegate { wb.Protect(); }); Assert.Throws(typeof(InvalidOperationException), delegate { wb.Unprotect(); }); Assert.Throws(typeof(ArgumentException), delegate { wb.Unprotect("Cde@345"); }); @@ -304,9 +307,11 @@ wb.Protect(); Assert.IsTrue(wb.LockStructure); Assert.IsFalse(wb.LockWindows); + Assert.IsFalse(wb.IsPasswordProtected); wb.Protect("Abc@123"); Assert.IsTrue(wb.LockStructure); Assert.IsFalse(wb.LockWindows); + Assert.IsTrue(wb.IsPasswordProtected); } } @@ -319,10 +324,12 @@ wb.Protect(true, false, "Abc@123"); Assert.IsTrue(wb.LockStructure); Assert.IsFalse(wb.LockWindows); + Assert.IsTrue(wb.IsPasswordProtected); wb.Unprotect("Abc@123"); Assert.IsFalse(wb.LockStructure); Assert.IsFalse(wb.LockWindows); + Assert.IsFalse(wb.IsPasswordProtected); } } } -} \ No newline at end of file +}