You've already forked lazarus-ccr
fpspreadsheet: Improved password handling and format detection for the decryption readers.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8913 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -156,7 +156,7 @@ type
|
|||||||
var AFontColor: TsColor);
|
var AFontColor: TsColor);
|
||||||
function ReadHeaderFooterText(ANode: TDOMNode): String;
|
function ReadHeaderFooterText(ANode: TDOMNode): String;
|
||||||
procedure ReadMetaData(ANode: TDOMNode);
|
procedure ReadMetaData(ANode: TDOMNode);
|
||||||
procedure ReadMetaInfManifest(ANode: TDOMNode; out IsEncrypted: Boolean);
|
procedure ReadMetaInfManifest(ANode: TDOMNode);
|
||||||
procedure ReadPictures(AStream: TStream);
|
procedure ReadPictures(AStream: TStream);
|
||||||
procedure ReadPrintRanges(ATableNode: TDOMNode; ASheet: TsBasicWorksheet);
|
procedure ReadPrintRanges(ATableNode: TDOMNode; ASheet: TsBasicWorksheet);
|
||||||
procedure ReadRowsAndCells(ATableNode: TDOMNode);
|
procedure ReadRowsAndCells(ATableNode: TDOMNode);
|
||||||
@ -177,6 +177,7 @@ type
|
|||||||
protected
|
protected
|
||||||
FPointSeparatorSettings: TFormatSettings;
|
FPointSeparatorSettings: TFormatSettings;
|
||||||
procedure AddBuiltinNumFormats; override;
|
procedure AddBuiltinNumFormats; override;
|
||||||
|
function NeedsPassword(AStream: TStream): Boolean; override;
|
||||||
procedure ReadAutomaticStyles(AStylesNode: TDOMNode);
|
procedure ReadAutomaticStyles(AStylesNode: TDOMNode);
|
||||||
procedure ReadMasterStyles(AStylesNode: TDOMNode);
|
procedure ReadMasterStyles(AStylesNode: TDOMNode);
|
||||||
procedure ReadNumFormats(AStylesNode: TDOMNode);
|
procedure ReadNumFormats(AStylesNode: TDOMNode);
|
||||||
@ -203,7 +204,6 @@ type
|
|||||||
|
|
||||||
function Decrypt(AStream: TStream; ADecryptionInfo: TsOpenDocManifestFileEntry;
|
function Decrypt(AStream: TStream; ADecryptionInfo: TsOpenDocManifestFileEntry;
|
||||||
APassword: String; ADestStream: TStream; out AErrorMsg: String): Boolean; virtual;
|
APassword: String; ADestStream: TStream; out AErrorMsg: String): Boolean; virtual;
|
||||||
function SupportsDecryption: Boolean; virtual;
|
|
||||||
function UnzipToStream(AStream: TStream; AZippedFile: String;
|
function UnzipToStream(AStream: TStream; AZippedFile: String;
|
||||||
ADestStream: TStream): Boolean; virtual;
|
ADestStream: TStream): Boolean; virtual;
|
||||||
function UnzipToStream(AStream: TStream; AZippedFile: String;
|
function UnzipToStream(AStream: TStream; AZippedFile: String;
|
||||||
@ -1630,6 +1630,22 @@ begin
|
|||||||
Result := -1;
|
Result := -1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Returns true if the file is password-protected, i.e. if there are entries
|
||||||
|
in the META-INF/manifest.xml while contain decryption information. }
|
||||||
|
function TsSpreadOpenDocReader.NeedsPassword(AStream: TStream): Boolean;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
Unused(AStream);
|
||||||
|
for i := 0 to FManifestFileEntries.Count-1 do
|
||||||
|
if TsOpenDocManifestFileEntry(FManifestFileEntries[i]).Encrypted then
|
||||||
|
begin
|
||||||
|
Result := true;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
Result := false;
|
||||||
|
end;
|
||||||
|
|
||||||
function TsSpreadOpenDocReader.NodeIsEmptyCell(ACellNode: TDOMNode): Boolean;
|
function TsSpreadOpenDocReader.NodeIsEmptyCell(ACellNode: TDOMNode): Boolean;
|
||||||
var
|
var
|
||||||
valuestr: String;
|
valuestr: String;
|
||||||
@ -2062,8 +2078,7 @@ end;
|
|||||||
|
|
||||||
// Reads the file META-INF/manifest.xml. It is never encrypted and contains
|
// Reads the file META-INF/manifest.xml. It is never encrypted and contains
|
||||||
// decryption information.
|
// decryption information.
|
||||||
procedure TsSpreadOpenDocReader.ReadMetaInfManifest(ANode: TDOMNode;
|
procedure TsSpreadOpenDocReader.ReadMetaInfManifest(ANode: TDOMNode);
|
||||||
out IsEncrypted: Boolean);
|
|
||||||
|
|
||||||
function GetAlgorithmName(ASubNode: TDOMNode; AttrName: String): String;
|
function GetAlgorithmName(ASubNode: TDOMNode; AttrName: String): String;
|
||||||
var
|
var
|
||||||
@ -2093,7 +2108,6 @@ var
|
|||||||
nodeName: String;
|
nodeName: String;
|
||||||
entry: TsOpenDocManifestFileEntry;
|
entry: TsOpenDocManifestFileEntry;
|
||||||
begin
|
begin
|
||||||
IsEncrypted := false;
|
|
||||||
while ANode <> nil do
|
while ANode <> nil do
|
||||||
begin
|
begin
|
||||||
nodeName := ANode.NodeName;
|
nodeName := ANode.NodeName;
|
||||||
@ -2107,7 +2121,6 @@ begin
|
|||||||
nodeName := encryptionDataNode.NodeName;
|
nodeName := encryptionDataNode.NodeName;
|
||||||
if nodeName = 'manifest:encryption-data' then
|
if nodeName = 'manifest:encryption-data' then
|
||||||
begin
|
begin
|
||||||
IsEncrypted := true;
|
|
||||||
entry.Encrypted := true;
|
entry.Encrypted := true;
|
||||||
entry.EncryptionData_ChecksumType := GetAlgorithmName(encryptionDataNode, 'manifest:checksum-type');
|
entry.EncryptionData_ChecksumType := GetAlgorithmName(encryptionDataNode, 'manifest:checksum-type');
|
||||||
entry.EncryptionData_Checksum := GetAttrValue(encryptionDataNode, 'manifest:checksum');
|
entry.EncryptionData_Checksum := GetAttrValue(encryptionDataNode, 'manifest:checksum');
|
||||||
@ -2924,9 +2937,8 @@ begin
|
|||||||
ReadXMLStream(Doc, XMLStream);
|
ReadXMLStream(Doc, XMLStream);
|
||||||
if Assigned(Doc) then
|
if Assigned(Doc) then
|
||||||
begin
|
begin
|
||||||
ReadMetaInfManifest(Doc.DocumentElement.FindNode('manifest:file-entry'), isEncrypted);
|
ReadMetaInfManifest(Doc.DocumentElement.FindNode('manifest:file-entry'));
|
||||||
if isEncrypted and not SupportsDecryption then
|
CheckPassword(XMLStream, APassword);
|
||||||
raise EFpSpreadsheetReader.Create('File is encrypted.');
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
finally
|
finally
|
||||||
@ -5612,13 +5624,6 @@ begin
|
|||||||
Result := false;
|
Result := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ If a descendant reader class supports decryption it must return true
|
|
||||||
here. The standard ods reader is not able to read encrypted file content. }
|
|
||||||
function TsSpreadOpenDocReader.SupportsDecryption: Boolean;
|
|
||||||
begin
|
|
||||||
Result := false;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TsSpreadOpenDocReader.UnzipToStream(AStream: TStream;
|
function TsSpreadOpenDocReader.UnzipToStream(AStream: TStream;
|
||||||
AZippedFile: String; ADestStream: TStream): Boolean;
|
AZippedFile: String; ADestStream: TStream): Boolean;
|
||||||
begin
|
begin
|
||||||
|
@ -48,12 +48,16 @@ type
|
|||||||
public
|
public
|
||||||
{ File format detection }
|
{ File format detection }
|
||||||
class function CheckFileFormat(AStream: TStream): boolean; virtual; abstract;
|
class function CheckFileFormat(AStream: TStream): boolean; virtual; abstract;
|
||||||
{ General writing methods }
|
{ General reading methods }
|
||||||
procedure ReadFromFile(AFileName: string; APassword: String = '';
|
procedure ReadFromFile(AFileName: string; APassword: String = '';
|
||||||
AParams: TsStreamParams = []); virtual; abstract;
|
AParams: TsStreamParams = []); virtual; abstract;
|
||||||
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
AParams: TsStreamParams = []); virtual; abstract;
|
AParams: TsStreamParams = []); virtual; abstract;
|
||||||
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); virtual; abstract;
|
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); virtual; abstract;
|
||||||
|
{ Related to password-protected files }
|
||||||
|
procedure CheckPassword(AStream: TStream; var APassword: String); virtual;
|
||||||
|
function NeedsPassword(AStream: TStream): Boolean; virtual;
|
||||||
|
function SupportsDecryption: Boolean; virtual;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TsBasicSpreadWriter }
|
{ TsBasicSpreadWriter }
|
||||||
@ -263,6 +267,53 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ TsBasicSpreadReader }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Checks whether the currently processed stream is password-protected.
|
||||||
|
If true, it checks whether the reader class supports decryption and makes
|
||||||
|
sure that a password is provided to the calling routine (ReadFromStream).
|
||||||
|
Exceptions are raised in the error cases.
|
||||||
|
Must be called at the beginning of ReadFromStream when the stream potentially
|
||||||
|
can be decrypted in order to provide the user a reasonable error message.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsBasicSpreadReader.CheckPassword(AStream: TStream;
|
||||||
|
var APassword: String);
|
||||||
|
begin
|
||||||
|
if NeedsPassword(AStream) then
|
||||||
|
begin
|
||||||
|
if SupportsDecryption then
|
||||||
|
begin
|
||||||
|
if (APassword = '') and Assigned(FWorkbook.OnQueryPassword) then
|
||||||
|
APassword := FWorkbook.OnQueryPassword();
|
||||||
|
if (APassword = '') then
|
||||||
|
raise EFpSpreadsheetReader.Create('Password required to open this workbook.');
|
||||||
|
end else
|
||||||
|
raise EFpSpreadsheetReader.Create('File is encrypted.');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Should return whether the workbook to be loaded is password-protected by
|
||||||
|
encryption.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsBasicSpreadReader.NeedsPassword(AStream: TStream): Boolean;
|
||||||
|
begin
|
||||||
|
Unused(AStream);
|
||||||
|
Result := false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Returns true if this reader class is able to decrypt a password-protected
|
||||||
|
workbook file/stream.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsBasicSpreadReader.SupportsDecryption: Boolean;
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
end;
|
||||||
|
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
{ TsBasicSpreadWriter }
|
{ TsBasicSpreadWriter }
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
@ -507,7 +558,9 @@ end;
|
|||||||
Opens the file and calls ReadFromStream. Data are stored in the workbook
|
Opens the file and calls ReadFromStream. Data are stored in the workbook
|
||||||
specified during construction.
|
specified during construction.
|
||||||
|
|
||||||
@param AFileName The input file name.
|
@param (AFileName The input file name.)
|
||||||
|
@param (APassword The password needed to open a password-protected workbook.
|
||||||
|
Note: Password-protected files are not supported by all readers.)
|
||||||
@see TsWorkbook
|
@see TsWorkbook
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsCustomSpreadReader.ReadFromFile(AFileName: string;
|
procedure TsCustomSpreadReader.ReadFromFile(AFileName: string;
|
||||||
@ -545,11 +598,9 @@ end;
|
|||||||
|
|
||||||
Its basic implementation here assumes that the stream is a TStringStream and
|
Its basic implementation here assumes that the stream is a TStringStream and
|
||||||
the data are provided by calling ReadFromStrings. This mechanism is valid
|
the data are provided by calling ReadFromStrings. This mechanism is valid
|
||||||
for wikitables.
|
for wiki-tables.
|
||||||
|
|
||||||
Data will be stored in the workbook defined at construction.
|
Data will be stored in the workbook defined at construction.
|
||||||
|
|
||||||
@param AData Workbook which is filled by the data from the stream.
|
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsCustomSpreadReader.ReadFromStream(AStream: TStream;
|
procedure TsCustomSpreadReader.ReadFromStream(AStream: TStream;
|
||||||
APassword: String; AParams: TsStreamParams = []);
|
APassword: String; AParams: TsStreamParams = []);
|
||||||
@ -573,7 +624,7 @@ end;
|
|||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Reads workbook data from a string list. This abstract implementation does
|
Reads workbook data from a string list. This abstract implementation does
|
||||||
nothing and raises an exception. Must be overridden, like for wikitables.
|
nothing and raises an exception. Must be overridden, like for wiki-tables.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsCustomSpreadReader.ReadFromStrings(AStrings: TStrings;
|
procedure TsCustomSpreadReader.ReadFromStrings(AStrings: TStrings;
|
||||||
AParams: TsStreamParams = []);
|
AParams: TsStreamParams = []);
|
||||||
@ -593,8 +644,8 @@ end;
|
|||||||
Creates an internal instance of the number format list according to the
|
Creates an internal instance of the number format list according to the
|
||||||
file format being read/written.
|
file format being read/written.
|
||||||
|
|
||||||
@param AWorkbook Workbook from with the file is written. This parameter is
|
@param (AWorkbook Workbook from which the file is written. This parameter is
|
||||||
passed from the workbook which creates the writer.
|
passed from the workbook which creates the writer.)
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
constructor TsCustomSpreadWriter.Create(AWorkbook: TsBasicWorkbook);
|
constructor TsCustomSpreadWriter.Create(AWorkbook: TsBasicWorkbook);
|
||||||
begin
|
begin
|
||||||
|
@ -1228,6 +1228,8 @@ type
|
|||||||
property Keywords: TStrings read FKeywords write FKeywords;
|
property Keywords: TStrings read FKeywords write FKeywords;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TsOnQueryPassword = function: String of object;
|
||||||
|
|
||||||
{@@ Basic worksheet class to avoid circular unit references. It has only those
|
{@@ Basic worksheet class to avoid circular unit references. It has only those
|
||||||
properties and methods which do not require any other unit than fpstypes. }
|
properties and methods which do not require any other unit than fpstypes. }
|
||||||
TsBasicWorksheet = class
|
TsBasicWorksheet = class
|
||||||
@ -1256,6 +1258,7 @@ type
|
|||||||
TsBasicWorkbook = class
|
TsBasicWorkbook = class
|
||||||
private
|
private
|
||||||
FLog: TStringList;
|
FLog: TStringList;
|
||||||
|
FOnQueryPassword: TsOnQueryPassword;
|
||||||
function GetErrorMsg: String;
|
function GetErrorMsg: String;
|
||||||
protected
|
protected
|
||||||
FFileName: String;
|
FFileName: String;
|
||||||
@ -1292,6 +1295,8 @@ type
|
|||||||
property Protection: TsWorkbookProtections read FProtection write FProtection;
|
property Protection: TsWorkbookProtections read FProtection write FProtection;
|
||||||
{@@ Units of row heights and column widths }
|
{@@ Units of row heights and column widths }
|
||||||
property Units: TsSizeUnits read FUnits;
|
property Units: TsSizeUnits read FUnits;
|
||||||
|
{@@ Event returning the password to open a password-protected workbook }
|
||||||
|
property OnQueryPassword: TsOnQueryPassword read FOnQueryPassword write FOnQueryPassword;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ Ancestor of the fpSpreadsheet exceptions }
|
{@@ Ancestor of the fpSpreadsheet exceptions }
|
||||||
|
@ -141,6 +141,8 @@ type
|
|||||||
protected
|
protected
|
||||||
FFirstNumFormatIndexInFile: Integer;
|
FFirstNumFormatIndexInFile: Integer;
|
||||||
procedure AddBuiltinNumFormats; override;
|
procedure AddBuiltinNumFormats; override;
|
||||||
|
class function IsEncrypted(AStream: TStream): Boolean;
|
||||||
|
function NeedsPassword(AStream: TStream): Boolean; override;
|
||||||
public
|
public
|
||||||
constructor Create(AWorkbook: TsBasicWorkbook); override;
|
constructor Create(AWorkbook: TsBasicWorkbook); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@ -359,6 +361,8 @@ const
|
|||||||
|
|
||||||
LAST_PALETTE_INDEX = 63;
|
LAST_PALETTE_INDEX = 63;
|
||||||
|
|
||||||
|
CFB_SIGNATURE = $E11AB1A1E011CFD0; // Compound File Binary Signature
|
||||||
|
|
||||||
type
|
type
|
||||||
TFillListData = class
|
TFillListData = class
|
||||||
PatternType: String;
|
PatternType: String;
|
||||||
@ -951,6 +955,25 @@ begin
|
|||||||
Result := TMemoryStream.Create;
|
Result := TMemoryStream.Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Checks the file header for the signature of the decrypted file format. }
|
||||||
|
class function TsSpreadOOXMLReader.IsEncrypted(AStream: TStream): Boolean;
|
||||||
|
var
|
||||||
|
p: Int64;
|
||||||
|
buf: Cardinal;
|
||||||
|
begin
|
||||||
|
p := AStream.Position;
|
||||||
|
AStream.Position := 0;
|
||||||
|
AStream.Read(buf, SizeOf(buf));
|
||||||
|
Result := (buf = Cardinal(CFB_SIGNATURE));
|
||||||
|
AStream.Position := p;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Returns TRUE if the file is encrypted and requires a password. }
|
||||||
|
function TsSpreadOOXMLReader.NeedsPassword(AStream: TStream): Boolean;
|
||||||
|
begin
|
||||||
|
Result := IsEncrypted(AStream);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TsSpreadOOXMLReader.ReadActiveSheet(ANode: TDOMNode;
|
procedure TsSpreadOOXMLReader.ReadActiveSheet(ANode: TDOMNode;
|
||||||
out ActiveSheetIndex: Integer);
|
out ActiveSheetIndex: Integer);
|
||||||
var
|
var
|
||||||
@ -4411,6 +4434,8 @@ begin
|
|||||||
Unused(APassword, AParams);
|
Unused(APassword, AParams);
|
||||||
Doc := nil;
|
Doc := nil;
|
||||||
|
|
||||||
|
CheckPassword(AStream, APassword);
|
||||||
|
|
||||||
try
|
try
|
||||||
// Retrieve theme colors
|
// Retrieve theme colors
|
||||||
XMLStream := CreateXMLStream;
|
XMLStream := CreateXMLStream;
|
||||||
|
@ -10,7 +10,7 @@ uses
|
|||||||
{$IFDEF UNZIP_ABBREVIA}
|
{$IFDEF UNZIP_ABBREVIA}
|
||||||
ABUnzper,
|
ABUnzper,
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
fpsTypes, fpsOpenDocument;
|
fpsTypes, fpsUtils, fpsOpenDocument;
|
||||||
|
|
||||||
type
|
type
|
||||||
TsSpreadOpenDocReaderCrypto = class(TsSpreadOpenDocReader)
|
TsSpreadOpenDocReaderCrypto = class(TsSpreadOpenDocReader)
|
||||||
@ -141,7 +141,6 @@ begin
|
|||||||
raise EFpSpreadsheetReader.Create('Unsupported key generation method ' + ADecryptionInfo.KeyDerivationName);
|
raise EFpSpreadsheetReader.Create('Unsupported key generation method ' + ADecryptionInfo.KeyDerivationName);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ Tells the calling routine that this reader is able to decrypt ods files. }
|
{ Tells the calling routine that this reader is able to decrypt ods files. }
|
||||||
function TsSpreadOpenDocReaderCrypto.SupportsDecryption: Boolean;
|
function TsSpreadOpenDocReaderCrypto.SupportsDecryption: Boolean;
|
||||||
begin
|
begin
|
||||||
|
@ -18,7 +18,7 @@ uses
|
|||||||
CFB_Signature = $E11AB1A1E011CFD0; // Compound File Binary Signature
|
CFB_Signature = $E11AB1A1E011CFD0; // Compound File Binary Signature
|
||||||
// Weird is the documentation is equal to
|
// Weird is the documentation is equal to
|
||||||
// $D0CF11E0A1B11AE1, but here is inversed
|
// $D0CF11E0A1B11AE1, but here is inversed
|
||||||
// maybe related to litle endian thing?!!
|
// maybe related to little endian thing?!!
|
||||||
|
|
||||||
// EncryptionHeaderFlags as defined in 2.3.1 [MS-OFFCRYPTO]
|
// EncryptionHeaderFlags as defined in 2.3.1 [MS-OFFCRYPTO]
|
||||||
ehfAES = $00000004;
|
ehfAES = $00000004;
|
||||||
|
@ -6,11 +6,17 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Classes,
|
Classes,
|
||||||
fpstypes, xlsxooxml, xlsxdecrypter;
|
fpstypes, fpsUtils, xlsxooxml, xlsxdecrypter;
|
||||||
|
|
||||||
type
|
type
|
||||||
TsSpreadOOXMLReaderCrypto = class(TsSpreadOOXMLReader)
|
TsSpreadOOXMLReaderCrypto = class(TsSpreadOOXMLReader)
|
||||||
|
private
|
||||||
|
FNeedsPassword: Boolean;
|
||||||
|
protected
|
||||||
|
function NeedsPassword(AStream: TStream): Boolean; override;
|
||||||
|
function SupportsDecryption: Boolean; override;
|
||||||
public
|
public
|
||||||
|
class function CheckFileFormat(AStream: TStream): boolean; override;
|
||||||
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
AParams: TsStreamParams = []); override;
|
AParams: TsStreamParams = []); override;
|
||||||
end;
|
end;
|
||||||
@ -24,17 +30,34 @@ implementation
|
|||||||
uses
|
uses
|
||||||
fpsReaderWriter;
|
fpsReaderWriter;
|
||||||
|
|
||||||
|
class function TsSpreadOOXMLReaderCrypto.CheckFileFormat(AStream: TStream): boolean;
|
||||||
|
begin
|
||||||
|
Result := inherited; // This checks for a normal xlsx format ...
|
||||||
|
if not Result then
|
||||||
|
Result := IsEncrypted(AStream); // ... and this for a decrypted one.
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsSpreadOOXMLReaderCrypto.NeedsPassword(AStream: TStream): Boolean;
|
||||||
|
begin
|
||||||
|
Unused(AStream);
|
||||||
|
Result := FNeedsPassword;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TsSpreadOOXMLReaderCrypto.ReadFromStream(AStream: TStream;
|
procedure TsSpreadOOXMLReaderCrypto.ReadFromStream(AStream: TStream;
|
||||||
APassword: String = ''; AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
ExcelDecrypt : TExcelFileDecryptor;
|
ExcelDecrypt : TExcelFileDecryptor;
|
||||||
DecryptedStream: TStream;
|
DecryptedStream: TStream;
|
||||||
begin
|
begin
|
||||||
|
FNeedsPassword := false;
|
||||||
|
|
||||||
ExcelDecrypt := TExcelFileDecryptor.Create;
|
ExcelDecrypt := TExcelFileDecryptor.Create;
|
||||||
try
|
try
|
||||||
AStream.Position := 0;
|
AStream.Position := 0;
|
||||||
if ExcelDecrypt.isEncryptedAndSupported(AStream) then
|
if ExcelDecrypt.isEncryptedAndSupported(AStream) then
|
||||||
begin
|
begin
|
||||||
|
FNeedsPassword := true;
|
||||||
|
CheckPassword(AStream, APassword);
|
||||||
DecryptedStream := TMemoryStream.Create;
|
DecryptedStream := TMemoryStream.Create;
|
||||||
try
|
try
|
||||||
ExcelDecrypt.Decrypt(AStream, DecryptedStream, UnicodeString(APassword));
|
ExcelDecrypt.Decrypt(AStream, DecryptedStream, UnicodeString(APassword));
|
||||||
@ -42,12 +65,9 @@ begin
|
|||||||
AStream.Free;
|
AStream.Free;
|
||||||
AStream := TMemoryStream.Create;
|
AStream := TMemoryStream.Create;
|
||||||
DecryptedStream.Position := 0;
|
DecryptedStream.Position := 0;
|
||||||
|
|
||||||
TMemoryStream(decryptedStream).SaveToFile('decr.zip');
|
|
||||||
DecryptedStream.Position := 0;
|
|
||||||
|
|
||||||
AStream.CopyFrom(DecryptedStream, DecryptedStream.Size);
|
AStream.CopyFrom(DecryptedStream, DecryptedStream.Size);
|
||||||
AStream.Position := 0;
|
AStream.Position := 0;
|
||||||
|
FNeedsPassword := false; // AStream is not encrypted any more.
|
||||||
finally
|
finally
|
||||||
DecryptedStream.Free;
|
DecryptedStream.Free;
|
||||||
end;
|
end;
|
||||||
@ -60,6 +80,11 @@ begin
|
|||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TsSpreadOOXMLReaderCrypto.SupportsDecryption: Boolean;
|
||||||
|
begin
|
||||||
|
Result := true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ unit fpspreadsheetctrls;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
LMessages, LResources, LCLVersion,
|
LCLType, LCLIntf, LCLProc, LCLVersion, LMessages, LResources,
|
||||||
Classes, Graphics, SysUtils, Controls, StdCtrls, ComCtrls, ValEdit, ActnList,
|
Classes, Types, Graphics, SysUtils, Controls, StdCtrls, ComCtrls, ValEdit, ActnList,
|
||||||
fpstypes, fpspreadsheet;
|
fpstypes, fpspreadsheet;
|
||||||
|
|
||||||
const
|
const
|
||||||
@ -70,6 +70,7 @@ type
|
|||||||
FPendingOperation: TsCopyOperation;
|
FPendingOperation: TsCopyOperation;
|
||||||
FOptions: TsWorkbookOptions;
|
FOptions: TsWorkbookOptions;
|
||||||
FOnError: TsWorkbookSourceErrorEvent;
|
FOnError: TsWorkbookSourceErrorEvent;
|
||||||
|
FOnQueryPassword: TsOnQueryPassword;
|
||||||
|
|
||||||
// Getters / setters
|
// Getters / setters
|
||||||
function GetFileFormat: TsSpreadsheetFormat;
|
function GetFileFormat: TsSpreadsheetFormat;
|
||||||
@ -96,10 +97,11 @@ type
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
procedure AbortSelection;
|
procedure AbortSelection;
|
||||||
|
function DoQueryPassword: String;
|
||||||
procedure DoShowError(const AErrorMsg: String);
|
procedure DoShowError(const AErrorMsg: String);
|
||||||
procedure InternalCreateNewWorkbook(AWorkbook: TsWorkbook = nil);
|
procedure InternalCreateNewWorkbook(AWorkbook: TsWorkbook = nil);
|
||||||
procedure InternalLoadFromFile(AFileName: string; AAutoDetect: Boolean;
|
procedure InternalLoadFromFile(AFileName: string; AAutoDetect: Boolean;
|
||||||
AFormatID: TsSpreadFormatID; AWorksheetIndex: Integer = -1);
|
AFormatID: TsSpreadFormatID; AWorksheetIndex: Integer; APassword: String);
|
||||||
procedure InternalLoadFromWorkbook(AWorkbook: TsWorkbook;
|
procedure InternalLoadFromWorkbook(AWorkbook: TsWorkbook;
|
||||||
AWorksheetIndex: Integer = -1);
|
AWorksheetIndex: Integer = -1);
|
||||||
procedure Loaded; override;
|
procedure Loaded; override;
|
||||||
@ -116,15 +118,13 @@ type
|
|||||||
public
|
public
|
||||||
procedure CreateNewWorkbook;
|
procedure CreateNewWorkbook;
|
||||||
|
|
||||||
|
procedure LoadFromProtectedSpreadsheetFile(AFileName: String;
|
||||||
|
AFormatID: TsSpreadFormatID; APassword: String; AWorksheetIndex: Integer = -1);
|
||||||
procedure LoadFromSpreadsheetFile(AFileName: string;
|
procedure LoadFromSpreadsheetFile(AFileName: string;
|
||||||
AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer = -1); overload;
|
AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer = -1); overload;
|
||||||
procedure LoadFromSpreadsheetFile(AFileName: string;
|
procedure LoadFromSpreadsheetFile(AFileName: string;
|
||||||
AFormatID: TsSpreadFormatID = sfidUnknown; AWorksheetIndex: Integer = -1); overload;
|
AFormatID: TsSpreadFormatID = sfidUnknown; AWorksheetIndex: Integer = -1); overload;
|
||||||
procedure LoadFromWorkbook(AWorkbook: TsWorkbook; AWorksheetIndex: Integer = -1);
|
procedure LoadFromWorkbook(AWorkbook: TsWorkbook; AWorksheetIndex: Integer = -1);
|
||||||
{
|
|
||||||
procedure LoadFromSpreadsheetFile(AFileName: string;
|
|
||||||
AWorksheetIndex: Integer = -1); overload;
|
|
||||||
}
|
|
||||||
|
|
||||||
procedure SaveToSpreadsheetFile(AFileName: string;
|
procedure SaveToSpreadsheetFile(AFileName: string;
|
||||||
AOverwriteExisting: Boolean = true); overload;
|
AOverwriteExisting: Boolean = true); overload;
|
||||||
@ -133,9 +133,6 @@ type
|
|||||||
procedure SaveToSpreadsheetFile(AFileName: string; AFormatID: TsSpreadFormatID;
|
procedure SaveToSpreadsheetFile(AFileName: string; AFormatID: TsSpreadFormatID;
|
||||||
AOverwriteExisting: Boolean = true); overload;
|
AOverwriteExisting: Boolean = true); overload;
|
||||||
|
|
||||||
// procedure DisableControls;
|
|
||||||
// procedure EnableControls;
|
|
||||||
|
|
||||||
procedure SelectCell(ASheetRow, ASheetCol: Cardinal);
|
procedure SelectCell(ASheetRow, ASheetCol: Cardinal);
|
||||||
procedure SelectWorksheet(AWorkSheet: TsWorksheet);
|
procedure SelectWorksheet(AWorkSheet: TsWorksheet);
|
||||||
|
|
||||||
@ -183,6 +180,8 @@ type
|
|||||||
{@@ A message box is displayey if an error occurs during loading of a
|
{@@ A message box is displayey if an error occurs during loading of a
|
||||||
spreadsheet. This behavior can be replaced by means of the event OnError. }
|
spreadsheet. This behavior can be replaced by means of the event OnError. }
|
||||||
property OnError: TsWorkbookSourceErrorEvent read FOnError write FOnError;
|
property OnError: TsWorkbookSourceErrorEvent read FOnError write FOnError;
|
||||||
|
{@@ Event fired when a password is required. Handler must return the pwd. }
|
||||||
|
property OnQueryPassword: TsOnQueryPassword read FOnQueryPassword write FOnQueryPassword;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -710,8 +709,7 @@ function ScalePPI(ALength: Integer): Integer;
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Types, Math, StrUtils, TypInfo, LCLType, LCLIntf, LCLProc,
|
Math, StrUtils, TypInfo, Dialogs, Forms, Clipbrd,
|
||||||
Dialogs, Forms, Clipbrd,
|
|
||||||
fpsStrings, fpsCrypto, fpsReaderWriter, fpsUtils, fpsNumFormat, fpsImages,
|
fpsStrings, fpsCrypto, fpsReaderWriter, fpsUtils, fpsNumFormat, fpsImages,
|
||||||
fpsHTMLUtils, fpsExprParser;
|
fpsHTMLUtils, fpsExprParser;
|
||||||
|
|
||||||
@ -1016,6 +1014,22 @@ begin
|
|||||||
SelectWorksheet(FWorksheet);
|
SelectWorksheet(FWorksheet);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TsWorkbookSource.DoQueryPassword: String;
|
||||||
|
var
|
||||||
|
crs: TCursor;
|
||||||
|
begin
|
||||||
|
crs := Screen.Cursor;
|
||||||
|
Screen.Cursor := crDefault;
|
||||||
|
try
|
||||||
|
if Assigned(FOnQueryPassword) then
|
||||||
|
Result := FOnQueryPassword()
|
||||||
|
else
|
||||||
|
Result := InputBox('Password required to open workbook', 'Password', '');
|
||||||
|
finally
|
||||||
|
Screen.Cursor := crs;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
An error has occured during loading of the workbook. Shows a message box by
|
An error has occured during loading of the workbook. Shows a message box by
|
||||||
default. But a different behavior can be obtained by means of the OnError
|
default. But a different behavior can be obtained by means of the OnError
|
||||||
@ -1137,20 +1151,23 @@ end;
|
|||||||
for the loader.
|
for the loader.
|
||||||
Is ignored when AAutoDetect is @false.)
|
Is ignored when AAutoDetect is @false.)
|
||||||
@param(AWorksheetIndex Index of the worksheet to be selected after loading.)
|
@param(AWorksheetIndex Index of the worksheet to be selected after loading.)
|
||||||
|
@param(APassword Password to open encrypted workbook. Note: this is
|
||||||
|
supported only by ods and xlsx readers.)
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorkbookSource.InternalLoadFromFile(AFileName: string;
|
procedure TsWorkbookSource.InternalLoadFromFile(AFileName: string;
|
||||||
AAutoDetect: Boolean; AFormatID: TsSpreadFormatID;
|
AAutoDetect: Boolean; AFormatID: TsSpreadFormatID; AWorksheetIndex: Integer;
|
||||||
AWorksheetIndex: Integer = -1);
|
APassword: String);
|
||||||
var
|
var
|
||||||
book: TsWorkbook;
|
book: TsWorkbook;
|
||||||
begin
|
begin
|
||||||
book := TsWorkbook.Create;
|
book := TsWorkbook.Create;
|
||||||
try
|
try
|
||||||
book.Options := FOptions;
|
book.Options := FOptions;
|
||||||
|
book.OnQueryPassword := @DoQueryPassword;
|
||||||
if AAutoDetect then
|
if AAutoDetect then
|
||||||
book.ReadfromFile(AFileName)
|
book.ReadfromFile(AFileName, APassword)
|
||||||
else
|
else
|
||||||
book.ReadFromFile(AFileName, AFormatID);
|
book.ReadFromFile(AFileName, AFormatID, APassword);
|
||||||
InternalLoadFromWorkbook(book, AWorksheetIndex);
|
InternalLoadFromWorkbook(book, AWorksheetIndex);
|
||||||
except
|
except
|
||||||
// book is normally used as current workbook. But it must be destroyed
|
// book is normally used as current workbook. But it must be destroyed
|
||||||
@ -1220,9 +1237,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Public spreadsheet loader to be used if file format is known.
|
Public loader of a spreadsheet file.
|
||||||
|
|
||||||
Call this methdd for both built-in and user-provided file formats.
|
Call this method for both built-in and user-provided file formats.
|
||||||
|
|
||||||
|
If the workbook is password-protected the password is prompted by a dialog.
|
||||||
|
|
||||||
@param(AFilename Name of the spreadsheet file to be loaded.)
|
@param(AFilename Name of the spreadsheet file to be loaded.)
|
||||||
@param(AFormatID Identifier of the spreadsheet file format assumed
|
@param(AFormatID Identifier of the spreadsheet file format assumed
|
||||||
@ -1236,7 +1255,7 @@ var
|
|||||||
autodetect: Boolean;
|
autodetect: Boolean;
|
||||||
begin
|
begin
|
||||||
autodetect := (AFormatID = sfidUnknown);
|
autodetect := (AFormatID = sfidUnknown);
|
||||||
InternalLoadFromFile(AFileName, autodetect, AFormatID, AWorksheetIndex);
|
InternalLoadFromFile(AFileName, autodetect, AFormatID, AWorksheetIndex, '');
|
||||||
end;
|
end;
|
||||||
(*
|
(*
|
||||||
{@@ ------------------------------------------------------------------------------
|
{@@ ------------------------------------------------------------------------------
|
||||||
@ -1272,6 +1291,26 @@ begin
|
|||||||
InternalLoadFromWorkbook(AWorkbook, AWorksheetIndex);
|
InternalLoadFromWorkbook(AWorkbook, AWorksheetIndex);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Public loader of a spreadsheet file.
|
||||||
|
|
||||||
|
Should be called in case of password-protected files when the password is
|
||||||
|
already known and no password dialog should appear.
|
||||||
|
|
||||||
|
Call this method for both built-in and user-provided file formats.
|
||||||
|
|
||||||
|
@param(AFilename Name of the spreadsheet file to be loaded.)
|
||||||
|
@param(AFormatID Identifier of the spreadsheet file format assumed
|
||||||
|
for the file.)
|
||||||
|
@param(AWorksheetIndex Index of the worksheet to be selected after loading.
|
||||||
|
(If empty then the active worksheet is loaded) )
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsWorkbookSource.LoadFromProtectedSpreadsheetFile(AFileName: String;
|
||||||
|
AFormatID: TsSpreadFormatID; APassword: String; AWorksheetIndex: Integer = -1);
|
||||||
|
begin
|
||||||
|
InternalLoadFromFile(AFileName, false, AFormatID, AWorksheetIndex, APassword);
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Notifies listeners of workbook, worksheet, cell, or selection changes.
|
Notifies listeners of workbook, worksheet, cell, or selection changes.
|
||||||
The changed item is identified by the parameter AChangedItems.
|
The changed item is identified by the parameter AChangedItems.
|
||||||
|
Reference in New Issue
Block a user