fpspreadsheet: Add ods reader for cell, sheet and workbook protection. Simplification in xlsx reader for sheet protection. Remove field HashValue from TsCryptoInfo (it is PasswordHash now)

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5791 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2017-03-05 23:05:03 +00:00
parent b04fd3f0b5
commit 16118290c5
8 changed files with 137 additions and 66 deletions

View File

@ -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;

View File

@ -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).
// <loext:table-protection loext:select-unprotected-cells="true" />
// <loext:table-protection loext:select-protected-cells="true" />
// <loext:table-protection />
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

View File

@ -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;
{@@ ----------------------------------------------------------------------------

View File

@ -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];

View File

@ -2087,7 +2087,6 @@ procedure InitCryptoInfo(out AValue: TsCryptoInfo);
begin
AValue.PasswordHash := '';
AValue.Algorithm := caUnknown;
AValue.HashValue := '';
AValue.SaltValue := '';
AValue.SpinCount := 0;
end;

View File

@ -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;

View File

@ -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) + '"';

View File

@ -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...)');