diff --git a/components/fpspreadsheet/source/common/fpscrypto.pas b/components/fpspreadsheet/source/common/fpscrypto.pas
index b652b0147..9fd4bbcd6 100644
--- a/components/fpspreadsheet/source/common/fpscrypto.pas
+++ b/components/fpspreadsheet/source/common/fpscrypto.pas
@@ -33,6 +33,7 @@ end;
function StrToAlgorithm(const AName: String): TsCryptoAlgorithm;
begin
case AName of
+ // Excel
'MD2' : Result := caMD2;
'MD4' : Result := caMD4;
'MD5' : Result := caMD5;
@@ -43,7 +44,15 @@ begin
'SHA-384' : Result := caSHA384;
'SHA-512' : Result := caSHA512;
'WHIRLPOOL' : Result := caWHIRLPOOL;
- else Result := caUnknown;
+ else
+ // Libre/OpenOffice
+ if pos('sha1', AName) > 0 then // http://www.w3.org/2000/09/xmldsig#sha1
+ Result := caSHA1
+ else
+ if pos('sha256', AName) > 0 then // http://www.w3.org/2000/09/xmldsig#sha256
+ Result := caSHA256
+ else
+ Result := caUnknown;
end;
end;
diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas
index 0d351fdd1..e3d7c327e 100644
--- a/components/fpspreadsheet/source/common/fpsopendocument.pas
+++ b/components/fpspreadsheet/source/common/fpsopendocument.pas
@@ -115,6 +115,7 @@ type
procedure ReadColumns(ATableNode: TDOMNode);
procedure ReadColumnStyle(AStyleNode: TDOMNode);
procedure ReadDateMode(SpreadSheetNode: TDOMNode);
+ procedure ReadDocumentProtection(ANode: TDOMNode);
procedure ReadFont(ANode: TDOMNode; var AFontName: String;
var AFontSize: Single; var AFontStyle: TsFontStyles; var AFontColor: TsColor;
var AFontPosition: TsFontPosition);
@@ -129,6 +130,7 @@ type
procedure ReadRowsAndCells(ATableNode: TDOMNode);
procedure ReadRowStyle(AStyleNode: TDOMNode);
procedure ReadShapes(ATableNode: TDOMNode);
+ procedure ReadSheetProtection(ANode: TDOMNode; ASheet: TsWorksheet);
procedure ReadTableStyle(AStyleNode: TDOMNode);
protected
@@ -285,8 +287,7 @@ uses
{$IFDEF FPS_VARISBOOL}
fpsPatches,
{$ENDIF}
- fpsStrings, fpsStreams, fpsClasses, fpsExprParser,
- fpsImages;
+ fpsStrings, fpsStreams, fpsCrypto, fpsClasses, fpsExprParser, fpsImages;
const
{ OpenDocument general XML constants }
@@ -2060,6 +2061,25 @@ begin
raise Exception.CreateFmt('Spreadsheet file corrupt: cannot handle null-date format %s', [NullDateSetting]);
end;
+procedure TsSpreadOpenDocReader.ReadDocumentProtection(ANode: TDOMNode);
+var
+ s: String;
+ cinfo: TsCryptoInfo;
+begin
+ if ANode = nil then
+ exit;
+
+ if GetAttrValue(ANode, 'table:structure-protected') = 'true' then
+ Workbook.Protection := Workbook.Protection + [bpLockStructure]
+ else
+ exit;
+
+ InitCryptoInfo(cinfo);
+ cinfo.PasswordHash := GetAttrValue(ANode, 'table:protection-key');
+ cinfo.Algorithm := StrToAlgorithm(GetAttrValue(ANode, 'table:protection-key-digest-algorithm'));
+ Workbook.CryptoInfo := cinfo;
+end;
+
{ Reads font data from an xml node and returns the font elements. }
procedure TsSpreadOpenDocReader.ReadFont(ANode: TDOMNode; var AFontName: String;
var AFontSize: Single; var AFontStyle: TsFontStyles; var AFontColor: TsColor;
@@ -2468,6 +2488,7 @@ begin
if not Assigned(SpreadSheetNode) then
raise Exception.Create('[TsSpreadOpenDocReader.ReadFromStream] Node "office:spreadsheet" not found.');
+ ReadDocumentProtection(SpreadsheetNode);
ReadDateMode(SpreadSheetNode);
//process each table (sheet)
@@ -2485,6 +2506,8 @@ begin
end;
FWorkSheet := FWorkbook.AddWorksheet(GetAttrValue(TableNode, 'table:name'), true);
tablestyleName := GetAttrValue(TableNode, 'table:style-name');
+ // Read protection
+ ReadSheetProtection(TableNode, FWorksheet);
// Collect embedded images
ReadShapes(TableNode);
// Collect column styles used
@@ -3882,6 +3905,50 @@ begin
end;
end;
+procedure TsSpreadOpenDocReader.ReadSheetProtection(ANode: TDOMNode;
+ ASheet: TsWorksheet);
+var
+ s: String;
+ sp: TsWorksheetProtections;
+ cinfo: TsCryptoInfo;
+ childNode: TDOMNode;
+ nodeName: String;
+begin
+ if ANode = nil then
+ exit;
+ s := GetAttrValue(ANode, 'table:protected');
+ if s = 'true' then begin
+ sp := DEFAULT_SHEET_PROTECTION;
+ Include(sp, spCells);
+
+ // These items are ALLOWED (unlike Excel where they are FORBIDDEN).
+ //
+ //
+ //
+ childNode := ANode.FirstChild;
+ while childNode <> nil do
+ begin
+ nodeName := childnode.NodeName;
+ if nodeName = 'loext:table-protection' then begin
+ s := GetAttrValue(childnode, 'loext:select-unprotected-cells');
+ if s='true' then Exclude(sp, spSelectUnlockedCells)
+ else Include(sp, spSelectUnlockedCells);
+ if s='false' then Exclude(sp, spSelectLockedCells)
+ else Include(sp, spSelectLockedCells);
+ end;
+ childNode := childNode.NextSibling;
+ end;
+ ASheet.Protection := sp;
+ ASheet.Protect(true);
+
+ InitCryptoInfo(cinfo);
+ cinfo.PasswordHash := GetAttrValue(ANode, 'table:protection-key');
+ cinfo.Algorithm := StrToAlgorithm(GetAttrValue(ANode, 'table:protection-key-digest-algorithm'));
+ ASheet.CryptoInfo := cinfo;
+ end else
+ ASheet.Protect(false);
+end;
+
procedure TsSpreadOpenDocReader.ReadStyles(AStylesNode: TDOMNode);
var
styleNode: TDOMNode;
@@ -4220,6 +4287,17 @@ begin
fmt.VertAlignment := vaBottom;
if fmt.VertAlignment <> vaDefault then
Include(fmt.UsedFormattingFields, uffVertAlign);
+
+ // Protection
+ s := GetAttrValue(styleChildNode, 'style:cell-protect');
+ if s = 'none' then
+ fmt.Protection := []
+ else if s = 'hidden-and-protected' then
+ fmt.Protection := [cpLockCell, cpHideFormulas]
+ else if s = 'protected' then
+ fmt.Protection := [cpLockCell]
+ else if s = 'formula-hidden' then
+ fmt.Protection := [cpHideFormulas];
end
else
if nodeName = 'style:paragraph-properties' then
diff --git a/components/fpspreadsheet/source/common/fpspreadsheet.pas b/components/fpspreadsheet/source/common/fpspreadsheet.pas
index 4d4549bc4..076e5ee12 100644
--- a/components/fpspreadsheet/source/common/fpspreadsheet.pas
+++ b/components/fpspreadsheet/source/common/fpspreadsheet.pas
@@ -1189,7 +1189,8 @@ begin
FActiveCellRow := UNASSIGNED_ROW_COL_INDEX;
FActiveCellCol := UNASSIGNED_ROW_COL_INDEX;
- FProtection := DEFAULT_SHEET_PROTECTIONS;
+ FProtection := DEFAULT_SHEET_PROTECTION;
+ InitCryptoInfo(FCryptoInfo);
FOptions := [soShowGridLines, soShowHeaders];
end;
@@ -7981,6 +7982,10 @@ begin
// Add default cell format
InitFormatRecord(fmt);
AddCellFormat(fmt);
+
+ // Protection
+ InitCryptoInfo(FCryptoInfo);
+ FProtection := [];
end;
{@@ ----------------------------------------------------------------------------
diff --git a/components/fpspreadsheet/source/common/fpstypes.pas b/components/fpspreadsheet/source/common/fpstypes.pas
index 9d0ad2bf2..d3a1641e3 100644
--- a/components/fpspreadsheet/source/common/fpstypes.pas
+++ b/components/fpspreadsheet/source/common/fpstypes.pas
@@ -650,7 +650,6 @@ type
TsCryptoInfo = record
PasswordHash: String;
Algorithm: TsCryptoAlgorithm;
- HashValue: string;
SaltValue: string;
SpinCount: Integer;
end;
@@ -680,7 +679,7 @@ const
spCells, spSort, spSelectLockedCells, spSelectUnlockedCells
{, spObjects, spPivotTables, spScenarios} ];
- DEFAULT_SHEET_PROTECTIONS = ALL_SHEET_PROTECTIONS - [spSelectLockedCells, spSelectUnlockedcells];
+ DEFAULT_SHEET_PROTECTION = ALL_SHEET_PROTECTIONS - [spSelectLockedCells, spSelectUnlockedcells];
DEFAULT_CELL_PROTECTION = [cpLockCell];
diff --git a/components/fpspreadsheet/source/common/fpsutils.pas b/components/fpspreadsheet/source/common/fpsutils.pas
index a1b8234f4..6900849a0 100644
--- a/components/fpspreadsheet/source/common/fpsutils.pas
+++ b/components/fpspreadsheet/source/common/fpsutils.pas
@@ -2087,7 +2087,6 @@ procedure InitCryptoInfo(out AValue: TsCryptoInfo);
begin
AValue.PasswordHash := '';
AValue.Algorithm := caUnknown;
- AValue.HashValue := '';
AValue.SaltValue := '';
AValue.SpinCount := 0;
end;
diff --git a/components/fpspreadsheet/source/common/xlscommon.pas b/components/fpspreadsheet/source/common/xlscommon.pas
index f8d87a70c..c4f24a7ce 100644
--- a/components/fpspreadsheet/source/common/xlscommon.pas
+++ b/components/fpspreadsheet/source/common/xlscommon.pas
@@ -2043,14 +2043,14 @@ begin
if AWorksheet = nil then begin
// Password for workbook protection
- cinfo := FWorkbook.CryptoInfo;
+ InitCryptoInfo(cinfo);
cinfo.PasswordHash := Format('%.4x', [hash]);
cinfo.Algorithm := caExcel;
FWorkbook.CryptoInfo := cinfo;
end else
begin
// Password for worksheet protection
- cinfo := AWorksheet.CryptoInfo;
+ InitCryptoInfo(cinfo);
cinfo.PasswordHash := Format('%.4x', [hash]);
cinfo.Algorithm := caExcel;
AWorksheet.CryptoInfo := cinfo;
diff --git a/components/fpspreadsheet/source/common/xlsxooxml.pas b/components/fpspreadsheet/source/common/xlsxooxml.pas
index 96f4df9ba..91bbca3f3 100644
--- a/components/fpspreadsheet/source/common/xlsxooxml.pas
+++ b/components/fpspreadsheet/source/common/xlsxooxml.pas
@@ -2006,7 +2006,7 @@ procedure TsSpreadOOXMLReader.ReadSheetProtection(ANode: TDOMNode;
var
s: String;
shc: TsCryptoInfo;
- shp0, shp1: TsWorksheetProtections;
+ shp: TsWorksheetProtections;
begin
if ANode = nil then
exit;
@@ -2036,100 +2036,83 @@ begin
end;
AWorksheet.CryptoInfo := shc;
- shp1 := []; // will get "1" to include
- shp0 := ALL_SHEET_PROTECTIONS; // will get "0" to exclude
+ shp := DEFAULT_SHEET_PROTECTION;
// Attribute not found -> property = false
s := GetAttrValue(ANode, 'sheet');
- if (s = '1') then
- Include(shp1, spCells) else
- Exclude(shp0, spCells);
+ if (s = '1') then Include(shp, spCells) else
+ if (s = '0') or (s = '') then Exclude(shp, spCells);
s := GetAttrValue(ANode, 'selectLockedCells');
- if (s = '1') then
- Include(shp1, spSelectLockedCells) else
- Exclude(shp0, spSelectLockedCells);
+ if (s = '1') then Include(shp, spSelectLockedCells) else
+ if (s = '0') or (s = '') then Exclude(shp, spSelectLockedCells);
s := GetAttrValue(ANode, 'selectUnlockedCells');
- if (s = '1') then
- Include(shp1, spSelectUnlockedCells) else
- Exclude(shp0, spSelectUnlockedCells);
+ if (s = '1') then Include(shp, spSelectUnlockedCells) else
+ if (s = '') or (s = '0') then Exclude(shp, spSelectUnlockedCells);
// these options are currently not supported by fpspreadsheet
{
s := GetAttrValue(ANode, 'objects');
- if (s = '1') then
- Include(shp1, spObjects) else
- Exclude(shp0, spObjects);
+ if (s = '1') then Include(shp, spObjects) else
+ if (s = '') or (s = '0') then Exclude(shp, spObjects);
s := GetAttrValue(ANode, 'scenarios');
- if (s = '1') then
- Include(shp1, spScenarios) else
- Exclude(shp0, spScenarios);
+ if (s = '1') then Include(shp, spScenarios) else
+ if (s = '') or (s = '0') then Exclude(shp, spScenarios);
}
// Attribute not found -> property = true
{
s := GetAttrValue(ANode, 'autoFilter');
- if (s = '0') then
- Exclude(shp1, spAutoFilter) else
- Include(shp0, spAutoFilter);
+ if (s = '0') then Exclude(shp, spAutoFilter) else
+ if (s = '') or (s = '1') then Include(shp, spAutoFilter);
}
s := GetAttrValue(ANode, 'deleteColumns');
- if (s = '0') then
- Exclude(shp0, spDeleteColumns) else
- Include(shp1, spDeleteColumns);
+ if (s = '0') then Exclude(shp, spDeleteColumns) else
+ if (s = '') or (s = '1') then Include(shp, spDeleteColumns);
s := GetAttrValue(ANode, 'deleteRows');
- if (s = '0') then
- Exclude(shp0, spDeleteRows) else
- Include(shp1, spDeleteRows);
+ if (s = '0') then Exclude(shp, spDeleteRows) else
+ if (s = '') or (s = '1') then Include(shp, spDeleteRows);
s := GetAttrValue(ANode, 'formatCells');
- if (s = '0') then
- Exclude(shp0, spFormatCells) else
- Include(shp1, spFormatCells);
+ if (s = '0') then Exclude(shp, spFormatCells) else
+ if (s = '') or (s = '1') then Include(shp, spFormatCells);
s := GetAttrValue(ANode, 'formatColumns');
- if (s = '0') then
- Exclude(shp0, spFormatColumns) else
- Include(shp1, spFormatColumns);
+ if (s = '0') then Exclude(shp, spFormatColumns) else
+ if (s = '') or (s = '1') then Include(shp, spFormatColumns);
s := GetAttrValue(ANode, 'formatRows');
- if (s = '0') then
- Exclude(shp0, spFormatRows) else
- Include(shp1, spFormatRows);
+ if (s = '0') then Exclude(shp, spFormatRows) else
+ if (s = '') or (s = '1') then Include(shp, spFormatRows);
s := GetAttrValue(ANode, 'insertColumns');
- if (s = '0') then
- Exclude(shp0, spInsertColumns) else
- Include(shp1, spInsertColumns);
+ if (s = '0') then Exclude(shp, spInsertColumns) else
+ if (s = '') or (s = '1') then Include(shp, spInsertColumns);
s := GetAttrValue(ANode, 'insertHyperlinks');
- if (s = '0') then
- Exclude(shp0, spInsertHyperlinks) else
- Include(shp1, spInsertHyperlinks);
+ if (s = '0') then Exclude(shp, spInsertHyperlinks) else
+ if (s = '') or (s = '1') then Include(shp, spInsertHyperlinks);
s := GetAttrValue(ANode, 'insertRows');
- if (s = '0') then
- Exclude(shp0, spInsertRows) else
- Include(shp1, spInsertRows);
+ if (s = '0') then Exclude(shp, spInsertRows) else
+ if (s = '') or (s = '1') then Include(shp, spInsertRows);
s := GetAttrValue(ANode, 'sort');
- if (s = '0') then
- Exclude(shp0, spSort) else
- Include(shp1, spSort);
+ if (s = '0') then Exclude(shp, spSort) else
+ if (s = '') or (s = '1') then Include(shp, spSort);
// Currently no pivottable support in fpspreadsheet
{
s := GetAttrValue(ANode, 'pivotTables');
- if (s = '0') then
- Exclude(shp0, spPivotTables) else
- Include(shp1, spPivotTables);
+ if (s = '0') then Exclude(shp, spPivotTables) else
+ if (s = '') or (s = '1') then Include(shp, spPivotTables);
}
- AWorksheet.Protection := shp0 + shp1;
+ AWorksheet.Protection := shp;
AWorksheet.Protect(true);
end;
@@ -3449,7 +3432,7 @@ begin
s := s + ' password="' + AWorksheet.CryptoInfo.PasswordHash + '"'
else
begin
- s := s + ' hashValue="' + AWorksheet.CryptoInfo.HashValue + '"';
+ s := s + ' hashValue="' + AWorksheet.CryptoInfo.PasswordHash + '"';
if AWorksheet.CryptoInfo.Algorithm <> caUnknown then
s := s + ' algorithmName="' + AlgorithmToStr(AWorksheet.CryptoInfo.Algorithm) + '"';
diff --git a/components/fpspreadsheet/source/visual/fpspreadsheetctrls.pas b/components/fpspreadsheet/source/visual/fpspreadsheetctrls.pas
index 622a7b995..c93d77ef0 100644
--- a/components/fpspreadsheet/source/visual/fpspreadsheetctrls.pas
+++ b/components/fpspreadsheet/source/visual/fpspreadsheetctrls.pas
@@ -3655,9 +3655,8 @@ begin
AStrings.Add('(-) CryptoInfo=');
AStrings.Add(Format(' PasswordHash=%s', [Workbook.CryptoInfo.PasswordHash]));
AStrings.Add(Format(' Algorithm=%s', [AlgorithmToStr(Workbook.CryptoInfo.Algorithm)]));
- AStrings.Add(Format(' HashValue=%s', [Workbook.CryptoInfo.HashValue]));
AStrings.Add(Format(' SaltValue=%s', [Workbook.CryptoInfo.SaltValue]));
- AStrings.Add(Format(' SplinCount=%d', [Workbook.CryptoInfo.SpinCount]));
+ AStrings.Add(Format(' SpinCount=%d', [Workbook.CryptoInfo.SpinCount]));
end else
AStrings.Add('(+) CryptoInfo=(dblclick for more...)');
@@ -3839,9 +3838,8 @@ begin
AStrings.Add('(-) CryptoInfo=');
AStrings.Add(Format(' PasswordHash=%s', [Worksheet.CryptoInfo.PasswordHash]));
AStrings.Add(Format(' Algorithm=%s', [AlgorithmToStr(Worksheet.CryptoInfo.Algorithm)]));
- AStrings.Add(Format(' HashValue=%s', [Worksheet.CryptoInfo.HashValue]));
AStrings.Add(Format(' SaltValue=%s', [Worksheet.CryptoInfo.SaltValue]));
- AStrings.Add(Format(' SplinCount=%d', [Worksheet.CryptoInfo.SpinCount]));
+ AStrings.Add(Format(' SpinCount=%d', [Worksheet.CryptoInfo.SpinCount]));
end else
AStrings.Add('(+) CryptoInfo=(dblclick for more...)');