diff --git a/components/cryptini/latest_stable/cryptini.lpk b/components/cryptini/latest_stable/cryptini.lpk
new file mode 100644
index 000000000..a31d78dc5
--- /dev/null
+++ b/components/cryptini/latest_stable/cryptini.lpk
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/cryptini/latest_stable/cryptini.pas b/components/cryptini/latest_stable/cryptini.pas
new file mode 100644
index 000000000..c769bc1fd
--- /dev/null
+++ b/components/cryptini/latest_stable/cryptini.pas
@@ -0,0 +1,14 @@
+{ This file was automatically created by Lazarus. Do not edit!
+ This source is only used to compile and install the package.
+ }
+
+unit cryptini;
+
+interface
+
+uses
+ ucryptini;
+
+implementation
+
+end.
diff --git a/components/cryptini/latest_stable/demo/cryptinitest.ico b/components/cryptini/latest_stable/demo/cryptinitest.ico
new file mode 100644
index 000000000..9f02c598b
Binary files /dev/null and b/components/cryptini/latest_stable/demo/cryptinitest.ico differ
diff --git a/components/cryptini/latest_stable/demo/cryptinitest.lpi b/components/cryptini/latest_stable/demo/cryptinitest.lpi
new file mode 100644
index 000000000..5c9c44dc9
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/cryptinitest.lpi
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/cryptini/latest_stable/demo/cryptinitest.lpr b/components/cryptini/latest_stable/demo/cryptinitest.lpr
new file mode 100644
index 000000000..0f9ae1357
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/cryptinitest.lpr
@@ -0,0 +1,51 @@
+program cryptinitest;
+{ Test App for cryptini unit
+
+ Copyright (C) 2016 Gordon Bamber minesadorada@gmail.com
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
+ for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+}
+
+{$ifdef Linux}
+ {$ifdef FPC_CROSSCOMPILING}
+ {$ifdef CPUARM}
+//if GUI on RPi, then uncomment
+//{$linklib GLESv2}
+ {$endif}
+ {$linklib libc_nonshared.a}
+ {$endif}
+{$endif}
+{$mode objfpc}{$H+}
+
+uses {$IFDEF UNIX} {$IFDEF UseCThreads}
+ cthreads, {$ENDIF} {$ENDIF}
+ Interfaces, // this includes the LCL widgetset
+ Forms,
+ umainform, umemoform, ukeydialog,
+uInputSectionValuesForm;
+
+{$R *.res}
+begin
+ Application.Title:='CryptINI Test';
+ RequireDerivedFormResource := True;
+ Application.Initialize;
+ Application.CreateForm(Tmainform, mainform);
+ Application.CreateForm(TShowINIForm, ShowINIForm);
+ Application.CreateForm(Tkeydialog, keydialog);
+ Application.CreateForm(TInputSectionValuesForm, InputSectionValuesForm);
+ Application.Run;
+end.
+
+
diff --git a/components/cryptini/latest_stable/demo/cryptinitest.lps b/components/cryptini/latest_stable/demo/cryptinitest.lps
new file mode 100644
index 000000000..3d9a8e81c
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/cryptinitest.lps
@@ -0,0 +1,500 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/cryptini/latest_stable/demo/cryptinitest.res b/components/cryptini/latest_stable/demo/cryptinitest.res
new file mode 100644
index 000000000..a09d0d387
Binary files /dev/null and b/components/cryptini/latest_stable/demo/cryptinitest.res differ
diff --git a/components/cryptini/latest_stable/demo/uinputsectionvaluesform.lfm b/components/cryptini/latest_stable/demo/uinputsectionvaluesform.lfm
new file mode 100644
index 000000000..c7efd9ec2
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/uinputsectionvaluesform.lfm
@@ -0,0 +1,86 @@
+object InputSectionValuesForm: TInputSectionValuesForm
+ Left = 1154
+ Height = 314
+ Top = 456
+ Width = 535
+ BorderIcons = [biSystemMenu]
+ BorderStyle = bsSingle
+ Caption = 'InputSectionValuesForm'
+ ClientHeight = 314
+ ClientWidth = 535
+ OnCreate = FormCreate
+ OnShow = FormShow
+ Position = poMainFormCenter
+ LCLVersion = '1.7'
+ object Grp_NewSectionValues: TGroupBox
+ Left = 0
+ Height = 43
+ Top = 0
+ Width = 535
+ Align = alTop
+ AutoSize = True
+ Caption = 'Input new section values'
+ ChildSizing.HorizontalSpacing = 10
+ ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
+ ChildSizing.Layout = cclLeftToRightThenTopToBottom
+ ChildSizing.ControlsPerLine = 4
+ ClientHeight = 23
+ ClientWidth = 531
+ TabOrder = 0
+ object Lbl_SectionName: TLabel
+ Left = 0
+ Height = 23
+ Top = 0
+ Width = 183
+ Caption = 'New Section Name:'
+ ParentColor = False
+ end
+ object edt_NewSectionName: TEdit
+ Left = 193
+ Height = 23
+ Top = 0
+ Width = 159
+ AutoSize = False
+ OnEditingDone = edt_NewSectionNameEditingDone
+ TabOrder = 0
+ Text = 'My New Section'
+ end
+ object lbl_Invisible1: TLabel
+ Left = 362
+ Height = 23
+ Top = 0
+ Width = 80
+ ParentColor = False
+ end
+ object lbl_Invisible2: TLabel
+ Left = 452
+ Height = 23
+ Top = 0
+ Width = 79
+ ParentColor = False
+ end
+ end
+ object cmd_Close: TBitBtn
+ Left = 432
+ Height = 26
+ Top = 272
+ Width = 90
+ AutoSize = True
+ Caption = '&Finished'
+ Kind = bkClose
+ ModalResult = 11
+ TabOrder = 1
+ end
+ object cmd_Cancel: TBitBtn
+ Left = 344
+ Height = 26
+ Top = 273
+ Width = 82
+ AutoSize = True
+ Cancel = True
+ DefaultCaption = True
+ Kind = bkCancel
+ ModalResult = 2
+ TabOrder = 2
+ end
+end
diff --git a/components/cryptini/latest_stable/demo/uinputsectionvaluesform.pas b/components/cryptini/latest_stable/demo/uinputsectionvaluesform.pas
new file mode 100644
index 000000000..8a3e51866
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/uinputsectionvaluesform.pas
@@ -0,0 +1,156 @@
+unit uInputSectionValuesForm;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+ Buttons;
+
+const
+ C_NUMBEROFCONTROLS = 8; // Only need to change it here
+
+type
+
+ { TInputSectionValuesForm }
+
+ TInputSectionValuesForm = class(TForm)
+ cmd_Cancel: TBitBtn;
+ cmd_Close: TBitBtn;
+ edt_NewSectionName: TEdit;
+ lbl_Invisible2: TLabel;
+ Lbl_SectionName: TLabel;
+ Grp_NewSectionValues: TGroupBox;
+ lbl_Invisible1: TLabel;
+ procedure edt_NewSectionNameEditingDone(Sender: TObject);
+ procedure FormCreate(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ private
+ procedure DisableSectionNameEdit;
+ procedure InitControls;
+ procedure ProcessCheckbox(Sender: TObject);
+ procedure ProcessValueEdit(Sender: TObject);
+ public
+ ValueLabelArray: array[0..C_NUMBEROFCONTROLS - 1] of TLabel;
+ IdentEditArray: array[0..C_NUMBEROFCONTROLS - 1] of TEdit;
+ ValueEditArray: array[0..C_NUMBEROFCONTROLS - 1] of TEdit;
+ ValueCheckBoxArray: array[0..C_NUMBEROFCONTROLS - 1] of TCheckBox;
+ sSectionName: string;
+ NumberOfControls: integer;
+ end;
+
+var
+ InputSectionValuesForm: TInputSectionValuesForm;
+
+implementation
+
+{$R *.lfm}
+
+{ TInputSectionValuesForm }
+procedure TInputSectionValuesForm.InitControls;
+var
+ iCount: integer;
+begin
+ BeginFormUpdate;
+ NumberOfControls := High(ValueLabelArray) + 1;
+ for iCount := Low(ValueLabelArray) to High(ValueLabelArray) do
+ begin
+ ValueLabelArray[iCount] := TLabel.Create(Grp_NewSectionValues);
+ ValueLabelArray[iCount].Caption := Format('Key and Value %d', [iCount + 1]);
+ ValueLabelArray[iCount].Tag := iCount;
+ ValueLabelArray[iCount].parent := Grp_NewSectionValues;
+
+ IdentEditArray[iCount] := TEdit.Create(Grp_NewSectionValues);
+ IdentEditArray[iCount].Text := 'Input Key here';
+ IdentEditArray[iCount].Tag := iCount;
+ // IdentEditArray[iCount].OnEditingDone:=@ProcessEdit;
+ IdentEditArray[iCount].parent := Grp_NewSectionValues;
+
+ ValueEditArray[iCount] := TEdit.Create(Grp_NewSectionValues);
+ ValueEditArray[iCount].Text := 'Input value here';
+ ValueEditArray[iCount].Tag := iCount;
+ ValueEditArray[iCount].OnEditingDone := @ProcessValueEdit;
+ ValueEditArray[iCount].parent := Grp_NewSectionValues;
+
+ ValueCheckBoxArray[iCount] := TCheckBox.Create(Grp_NewSectionValues);
+ ValueCheckBoxArray[iCount].Caption := 'Integer?';
+ ValueCheckBoxArray[iCount].Checked := False;
+ ValueCheckBoxArray[iCount].Tag := iCount;
+ ValueCheckBoxArray[iCount].TabStop:=FALSE;
+ ValueCheckBoxArray[iCount].OnClick := @ProcessCheckbox;
+ ValueCheckBoxArray[iCount].parent := Grp_NewSectionValues;
+ end;
+ EndFormUpdate;
+end;
+
+procedure TInputSectionValuesForm.ProcessCheckbox(Sender: TObject);
+// Triggered by OnClick event
+var
+ TempCheckBox: TCheckBox;
+ iTag, iTest: integer;
+begin
+ TempCheckBox := Sender as TCheckBox;
+ iTag := TempCheckBox.Tag;
+ // Validate associated Edit text
+ if ValueCheckBoxArray[iTag].Checked then
+ if (TryStrToInt(ValueEditArray[iTag].Text, iTest) = False) then
+ begin
+ ValueCheckBoxArray[iTag].Checked := False;
+ ShowMessageFmt('%s is not an Integer! Please edit it first.',
+ [ValueEditArray[iTag].Text]);
+ end;
+end;
+
+procedure TInputSectionValuesForm.ProcessValueEdit(Sender: TObject);
+// Triggered by OnEditingDone event
+var
+ TempEdit: TEdit;
+ iTag, iTest: integer;
+
+begin
+ TempEdit := Sender as TEdit;
+ iTag := TempEdit.Tag;
+ // Auto check/uncheck associated checkbox
+ if TryStrToInt(TempEdit.Text, iTest) then
+ ValueCheckBoxArray[iTag].Checked := True
+ else
+ ValueCheckBoxArray[iTag].Checked := False;
+ // cmd_Close.SetFocus; // Enable this if you put a ShowMessage in this proc
+end;
+
+procedure TInputSectionValuesForm.DisableSectionNameEdit;
+begin
+ edt_NewSectionName.Enabled := False;
+end;
+
+procedure TInputSectionValuesForm.FormCreate(Sender: TObject);
+begin
+ Caption := Application.Title + ' - Make New Section';
+ Icon := Application.Icon;
+ InitControls;
+ sSectionName := edt_NewSectionName.Text;
+end;
+
+procedure TInputSectionValuesForm.edt_NewSectionNameEditingDone(Sender: TObject);
+begin
+ sSectionName := edt_NewSectionName.Text;
+end;
+
+procedure TInputSectionValuesForm.FormShow(Sender: TObject);
+var
+ iCount: integer;
+begin
+ // Reinitialise controls
+ edt_NewSectionName.Enabled := True;
+ for iCount := 0 to NumberOfControls - 1 do
+ begin
+ ;
+ IdentEditArray[iCount].Text := Format('Input Key %d here', [iCount + 1]);
+ ValueEditArray[iCount].Text := Format('Input value %d here', [iCount + 1]);
+ ValueCheckBoxArray[iCount].Checked := False;
+ end;
+ edt_NewSectionName.Text := 'My New Section';
+end;
+
+end.
diff --git a/components/cryptini/latest_stable/demo/ukeydialog.lfm b/components/cryptini/latest_stable/demo/ukeydialog.lfm
new file mode 100644
index 000000000..03483d39d
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/ukeydialog.lfm
@@ -0,0 +1,59 @@
+object keydialog: Tkeydialog
+ Left = 359
+ Height = 132
+ Top = 268
+ Width = 320
+ BorderIcons = [biSystemMenu]
+ BorderStyle = bsDialog
+ Caption = 'keydialog'
+ ClientHeight = 132
+ ClientWidth = 320
+ DefaultMonitor = dmMainForm
+ OnCreate = FormCreate
+ OnShow = FormShow
+ Position = poMainFormCenter
+ ShowInTaskBar = stNever
+ LCLVersion = '1.7'
+ object lbl_info: TLabel
+ Left = 0
+ Height = 15
+ Top = 16
+ Width = 320
+ Alignment = taCenter
+ AutoSize = False
+ Caption = 'Choose either a Phrase or a number as your key'
+ ParentColor = False
+ end
+ object edt_key: TEdit
+ Left = 35
+ Height = 23
+ Top = 40
+ Width = 250
+ TabOrder = 0
+ Text = 'Type a phrase or number'
+ end
+ object BitBtn1: TBitBtn
+ Left = 123
+ Height = 26
+ Top = 80
+ Width = 82
+ AutoSize = True
+ Cancel = True
+ DefaultCaption = True
+ Kind = bkCancel
+ ModalResult = 2
+ TabOrder = 1
+ end
+ object BitBtn2: TBitBtn
+ Left = 216
+ Height = 26
+ Top = 80
+ Width = 62
+ AutoSize = True
+ Default = True
+ DefaultCaption = True
+ Kind = bkOK
+ ModalResult = 1
+ TabOrder = 2
+ end
+end
diff --git a/components/cryptini/latest_stable/demo/ukeydialog.pas b/components/cryptini/latest_stable/demo/ukeydialog.pas
new file mode 100644
index 000000000..e319fc494
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/ukeydialog.pas
@@ -0,0 +1,51 @@
+unit ukeydialog;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, Forms, StdCtrls,
+ Buttons;
+
+type
+
+ { Tkeydialog }
+
+ Tkeydialog = class(TForm)
+ BitBtn1: TBitBtn;
+ BitBtn2: TBitBtn;
+ edt_key: TEdit;
+ lbl_info: TLabel;
+ procedure FormCreate(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ private
+
+ public
+ Var sKeyPhrase:String;
+ end;
+
+var
+ keydialog: Tkeydialog;
+
+implementation
+
+{$R *.lfm}
+
+{ Tkeydialog }
+
+procedure Tkeydialog.FormCreate(Sender: TObject);
+begin
+ Caption := Application.Title + ' Key Chooser';
+ Icon := Application.Icon;
+ sKeyPhrase:='Type a phrase or number';
+ edt_key.Text:=sKeyPhrase;
+end;
+
+procedure Tkeydialog.FormShow(Sender: TObject);
+begin
+ edt_key.Text:=sKeyPhrase;
+end;
+
+end.
+
diff --git a/components/cryptini/latest_stable/demo/umainform.lfm b/components/cryptini/latest_stable/demo/umainform.lfm
new file mode 100644
index 000000000..d4f74d5ff
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/umainform.lfm
@@ -0,0 +1,348 @@
+object mainform: Tmainform
+ Left = 874
+ Height = 327
+ Top = 158
+ Width = 469
+ BorderIcons = [biSystemMenu, biMinimize]
+ BorderStyle = bsSingle
+ Caption = 'mainform'
+ ClientHeight = 307
+ ClientWidth = 469
+ Menu = MainMenu1
+ OnCreate = FormCreate
+ OnDestroy = FormDestroy
+ OnShow = FormShow
+ Position = poScreenCenter
+ LCLVersion = '1.7'
+ object lbl_Section: TLabel
+ Left = 8
+ Height = 15
+ Top = 88
+ Width = 74
+ Caption = 'Section Name'
+ ParentColor = False
+ end
+ object edt_Section: TEdit
+ Left = 88
+ Height = 23
+ Top = 88
+ Width = 195
+ Font.Style = [fsBold]
+ ParentFont = False
+ TabOrder = 0
+ Text = 'TestSection'
+ end
+ object lbl_Ident: TLabel
+ Left = 16
+ Height = 15
+ Top = 116
+ Width = 51
+ Caption = 'Test Ident'
+ ParentColor = False
+ end
+ object edt_Ident: TEdit
+ Left = 88
+ Height = 23
+ Top = 116
+ Width = 195
+ TabOrder = 1
+ Text = 'Password'
+ end
+ object lbl_Value: TLabel
+ Left = 16
+ Height = 15
+ Top = 144
+ Width = 55
+ Caption = 'Test String'
+ ParentColor = False
+ end
+ object edt_Value: TEdit
+ Left = 88
+ Height = 23
+ Top = 144
+ Width = 195
+ TabOrder = 2
+ Text = 'mypassword'
+ end
+ object cmd_Close: TBitBtn
+ Left = 368
+ Height = 26
+ Top = 263
+ Width = 75
+ AutoSize = True
+ DefaultCaption = True
+ Kind = bkClose
+ ModalResult = 11
+ OnClick = mnu_fileCloseClick
+ TabOrder = 3
+ end
+ object lbl_Integer: TLabel
+ Left = 16
+ Height = 15
+ Top = 172
+ Width = 61
+ Caption = 'Test Integer'
+ ParentColor = False
+ end
+ object edt_Integer: TEdit
+ Left = 88
+ Height = 23
+ Top = 172
+ Width = 195
+ OnEditingDone = edt_IntegerEditingDone
+ TabOrder = 4
+ Text = '12345'
+ end
+ object cmd_ShowINI: TButton
+ Left = 16
+ Height = 25
+ Top = 264
+ Width = 186
+ Caption = 'Show/Edit INI'
+ OnClick = cmd_ShowINIClick
+ TabOrder = 5
+ end
+ object rg_Encryption: TRadioGroup
+ Left = 16
+ Height = 58
+ Top = 200
+ Width = 76
+ AutoFill = True
+ AutoSize = True
+ Caption = 'Encryption'
+ ChildSizing.LeftRightSpacing = 6
+ ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
+ ChildSizing.EnlargeVertical = crsHomogenousChildResize
+ ChildSizing.ShrinkHorizontal = crsScaleChilds
+ ChildSizing.ShrinkVertical = crsScaleChilds
+ ChildSizing.Layout = cclLeftToRightThenTopToBottom
+ ChildSizing.ControlsPerLine = 1
+ ClientHeight = 38
+ ClientWidth = 72
+ ItemIndex = 0
+ Items.Strings = (
+ 'On'
+ 'Off'
+ )
+ OnSelectionChanged = rg_EncryptionSelectionChanged
+ TabOrder = 6
+ end
+ object rg_SectionHashing: TRadioGroup
+ Left = 96
+ Height = 58
+ Top = 200
+ Width = 105
+ AutoFill = True
+ AutoSize = True
+ Caption = 'Section Hashing'
+ ChildSizing.LeftRightSpacing = 6
+ ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
+ ChildSizing.EnlargeVertical = crsHomogenousChildResize
+ ChildSizing.ShrinkHorizontal = crsScaleChilds
+ ChildSizing.ShrinkVertical = crsScaleChilds
+ ChildSizing.Layout = cclLeftToRightThenTopToBottom
+ ChildSizing.ControlsPerLine = 1
+ ClientHeight = 38
+ ClientWidth = 101
+ ItemIndex = 0
+ Items.Strings = (
+ 'On'
+ 'Off'
+ )
+ OnSelectionChanged = rg_SectionHashingSelectionChanged
+ TabOrder = 7
+ end
+ object GroupBox1: TGroupBox
+ Left = 304
+ Height = 163
+ Top = 8
+ Width = 139
+ AutoSize = True
+ Caption = 'Section Tests'
+ ChildSizing.VerticalSpacing = 5
+ ChildSizing.Layout = cclLeftToRightThenTopToBottom
+ ChildSizing.ControlsPerLine = 1
+ ClientHeight = 143
+ ClientWidth = 135
+ TabOrder = 8
+ object cmd_WriteSection: TButton
+ Left = 0
+ Height = 25
+ Top = 0
+ Width = 135
+ AutoSize = True
+ Caption = 'Write Whole Section'
+ OnClick = cmd_WriteSectionClick
+ TabOrder = 0
+ end
+ object cmd_ReadSectionValues: TButton
+ Left = 0
+ Height = 25
+ Top = 30
+ Width = 135
+ AutoSize = True
+ Caption = 'Read Whole Section'
+ OnClick = cmd_ReadSectionValuesClick
+ TabOrder = 1
+ end
+ object cmd_VerifySectionValues: TButton
+ Left = 0
+ Height = 25
+ Top = 60
+ Width = 135
+ AutoSize = True
+ Caption = 'Verify Whole Section'
+ OnClick = cmd_VerifySectionValuesClick
+ TabOrder = 2
+ end
+ object cmd_EraseSection: TButton
+ Left = 0
+ Height = 25
+ Top = 90
+ Width = 135
+ AutoSize = True
+ Caption = 'Erase Whole Section'
+ OnClick = cmd_EraseSectionClick
+ TabOrder = 3
+ end
+ object cmb_Sections: TComboBox
+ Left = 0
+ Height = 23
+ Top = 120
+ Width = 135
+ ItemHeight = 15
+ OnSelect = cmb_SectionsSelect
+ Style = csDropDownList
+ TabOrder = 4
+ end
+ end
+ object Grp_DefaultValueTests: TGroupBox
+ Left = 16
+ Height = 70
+ Top = 8
+ Width = 284
+ AutoSize = True
+ Caption = 'Default Value Tests'
+ ChildSizing.Layout = cclLeftToRightThenTopToBottom
+ ChildSizing.ControlsPerLine = 3
+ ClientHeight = 50
+ ClientWidth = 280
+ TabOrder = 9
+ object cmd_Write: TButton
+ Left = 0
+ Height = 25
+ Top = 0
+ Width = 91
+ AutoSize = True
+ Caption = 'Write Values'
+ OnClick = cmd_WriteClick
+ TabOrder = 0
+ end
+ object cmd_Read: TButton
+ Left = 91
+ Height = 25
+ Top = 0
+ Width = 96
+ AutoSize = True
+ Caption = 'Read Values'
+ OnClick = cmd_ReadClick
+ TabOrder = 1
+ end
+ object cmd_Verify: TButton
+ Left = 187
+ Height = 25
+ Top = 0
+ Width = 93
+ AutoSize = True
+ Caption = 'Verify Values'
+ OnClick = cmd_VerifyClick
+ TabOrder = 2
+ end
+ object cmd_ValueExists: TButton
+ Left = 0
+ Height = 25
+ Top = 25
+ Width = 91
+ AutoSize = True
+ Caption = 'Values Exist'
+ OnClick = cmd_ValueExistsClick
+ TabOrder = 3
+ end
+ object cmd_DeleteValue: TButton
+ Left = 91
+ Height = 25
+ Top = 25
+ Width = 96
+ AutoSize = True
+ Caption = 'Delete Values'
+ OnClick = cmd_DeleteValueClick
+ TabOrder = 4
+ end
+ end
+ object grp_convert: TGroupBox
+ Left = 304
+ Height = 45
+ Top = 176
+ Width = 133
+ AutoSize = True
+ Caption = 'Upgrade existing INI'
+ ChildSizing.Layout = cclLeftToRightThenTopToBottom
+ ChildSizing.ControlsPerLine = 1
+ ClientHeight = 25
+ ClientWidth = 129
+ TabOrder = 10
+ object cmd_convertToCryptini: TButton
+ Left = 0
+ Height = 25
+ Top = 0
+ Width = 129
+ AutoSize = True
+ Caption = 'Convert to CryptINI'
+ OnClick = cmd_convertToCryptiniClick
+ TabOrder = 0
+ end
+ end
+ object MainMenu1: TMainMenu
+ Left = 432
+ object mnu_file: TMenuItem
+ Caption = '&File'
+ object mnu_fileClose: TMenuItem
+ Caption = 'E&xit'
+ OnClick = mnu_fileCloseClick
+ end
+ end
+ object mnu_options: TMenuItem
+ Caption = '&Options'
+ object mnu_optionsEncryptionKey: TMenuItem
+ Caption = 'Integer En&cryption key...'
+ OnClick = mnu_optionsEncryptionKeyClick
+ end
+ object mnu_optionsEncryptINIFile: TMenuItem
+ Caption = 'Encrypt INI file'
+ OnClick = mnu_optionsEncryptINIFileClick
+ end
+ object mnu_optionsDecryptINIFile: TMenuItem
+ Caption = 'Decrypt INI file'
+ OnClick = mnu_optionsDecryptINIFileClick
+ end
+ end
+ object mnu_help: TMenuItem
+ Caption = '&Help'
+ object mnu_helpHelp: TMenuItem
+ Caption = 'Help'
+ OnClick = mnu_helpHelpClick
+ end
+ object mnu_helpAbout: TMenuItem
+ Caption = '&About..'
+ OnClick = mnu_helpAboutClick
+ end
+ end
+ end
+ object OpenDialog1: TOpenDialog
+ Title = 'Open existing INI file'
+ Filter = 'INI file|*.ini|Any File|*.*'
+ Options = [ofReadOnly, ofNoTestFileCreate, ofEnableSizing, ofViewDetail]
+ Left = 384
+ Top = 8
+ end
+end
diff --git a/components/cryptini/latest_stable/demo/umainform.pas b/components/cryptini/latest_stable/demo/umainform.pas
new file mode 100644
index 000000000..03905ac4c
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/umainform.pas
@@ -0,0 +1,993 @@
+unit umainform;
+
+{$DEFINE USE_DCPCRYPT}// Delete this if you don't have the DCrypt library
+// It enables the 'Encrypt INI' and 'Decrypt INI' menu entries
+// {$DEFINE USE_THREADSAFE} // Enable this for the thread-safe version
+
+{ Test App for cryptini unit
+
+ Copyright (C) 2016 Gordon Bamber minesadorada@gmail.com
+ Encrypt/Decrypt INI code: @Ericktux (http://forum.lazarus.freepascal.org)
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
+ for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+}
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ SysUtils, LazFileUtils, FileUtil, Forms, Dialogs, StdCtrls, Controls, Classes,
+ Buttons, ExtCtrls, Menus, ucryptini, umemoform, ukeydialog,
+ uInputSectionValuesForm;
+
+const
+ {$IFDEF WINDOWS}
+ C_OS = 'win';
+ {$ELSE}
+ C_OS = 'linux';
+ {$ENDIF}
+ {$IFDEF CPU32}
+ C_BITNESS = '32';
+ {$ELSE}
+ C_BITNESS = '64';
+ {$ENDIF}
+ C_PFX = C_OS + C_BITNESS;
+ C_KEYPHRASE = 'Rudolph the Red Nosed Reindeer: had a very shiny nose';
+
+ C_VERSION = '1.0.0.5';
+
+type
+
+ { Tmainform }
+
+ Tmainform = class(TForm)
+ cmd_convertToCryptini: TButton;
+ cmd_DeleteValue: TButton;
+ cmd_EraseSection: TButton;
+ cmd_Read: TButton;
+ cmd_ReadSectionValues: TButton;
+ cmd_ShowINI: TButton;
+ cmd_Close: TBitBtn;
+ cmd_ValueExists: TButton;
+ cmd_Verify: TButton;
+ cmd_VerifySectionValues: TButton;
+ cmd_Write: TButton;
+ cmd_WriteSection: TButton;
+ cmb_Sections: TComboBox;
+ edt_Value: TEdit;
+ edt_Section: TEdit;
+ edt_Ident: TEdit;
+ edt_Integer: TEdit;
+ GroupBox1: TGroupBox;
+ grp_convert: TGroupBox;
+ Grp_DefaultValueTests: TGroupBox;
+ lbl_Value: TLabel;
+ lbl_Section: TLabel;
+ lbl_Ident: TLabel;
+ lbl_Integer: TLabel;
+ MainMenu1: TMainMenu;
+ mnu_optionsDecryptINIFile: TMenuItem;
+ mnu_optionsEncryptINIFile: TMenuItem;
+ mnu_helpAbout: TMenuItem;
+ mnu_helpHelp: TMenuItem;
+ mnu_help: TMenuItem;
+ mnu_optionsEncryptionKey: TMenuItem;
+ mnu_fileClose: TMenuItem;
+ mnu_options: TMenuItem;
+ mnu_file: TMenuItem;
+ OpenDialog1: TOpenDialog;
+ rg_Encryption: TRadioGroup;
+ rg_SectionHashing: TRadioGroup;
+ procedure cmb_SectionsSelect(Sender: TObject);
+ procedure cmd_convertToCryptiniClick(Sender: TObject);
+ procedure cmd_DeleteValueClick(Sender: TObject);
+ procedure cmd_ReadClick(Sender: TObject);
+ procedure cmd_ReadSectionValuesClick(Sender: TObject);
+ procedure cmd_ShowINIClick(Sender: TObject);
+ procedure cmd_ValueExistsClick(Sender: TObject);
+ procedure cmd_VerifyClick(Sender: TObject);
+ procedure cmd_VerifySectionValuesClick(Sender: TObject);
+ procedure cmd_WriteClick(Sender: TObject);
+ procedure cmd_EraseSectionClick(Sender: TObject);
+ procedure cmd_WriteSectionClick(Sender: TObject);
+ procedure edt_IntegerEditingDone(Sender: TObject);
+ procedure FormCreate(Sender: TObject);
+ procedure FormDestroy(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ procedure mnu_fileCloseClick(Sender: TObject);
+ procedure mnu_helpAboutClick(Sender: TObject);
+ procedure mnu_helpHelpClick(Sender: TObject);
+ {$IFDEF USE_DCPCRYPT}
+ procedure mnu_optionsDecryptINIFileClick(Sender: TObject);
+ procedure mnu_optionsEncryptINIFileClick(Sender: TObject);
+ {$ENDIF}
+ procedure mnu_optionsEncryptionKeyClick(Sender: TObject);
+ procedure rg_EncryptionSelectionChanged(Sender: TObject);
+ procedure rg_SectionHashingSelectionChanged(Sender: TObject);
+ private
+ IniFilePath: string;
+ {$IFDEF USE_THREADSAFE}
+ INI: TLockCryptIniFile;
+ {$ELSE}
+ INI: TCryptIniFile;
+ {$ENDIF}
+ sStoredMD5Hash: string;
+ sVersion: string;
+ public
+
+ end;
+
+var
+ mainform: Tmainform;
+
+implementation
+
+{$R *.lfm}
+
+{ Tmainform }
+
+procedure Tmainform.FormCreate(Sender: TObject);
+begin
+ IniFilePath := ProgramDirectory + 'test' + C_PFX +
+{$IFDEF WINDOWS}
+ '.' +
+{$ENDIF}
+ 'ini';
+
+{$IFNDEF USE_DCPCRYPT}
+ mnu_optionsEncryptINIFile.Enabled := False;
+ mnu_optionsDecryptINIFile.Enabled := False;
+{$ENDIF}
+
+ {DEBUG - delete any old versions of the INI
+ if FileExists(IniFilePath) then
+ DeleteFile(IniFilePath);
+ }
+ Caption := Application.Title;
+ Icon := Application.Icon;
+ {$IFDEF USE_THREADSAFE}
+ INI := TLockCryptIniFile.Create(IniFilePath);
+ INI.Lock;
+ {$ELSE}
+ INI := TCryptIniFile.Create(IniFilePath);
+ {$ENDIF}
+
+ // Create encryption key for secure Read/WriteInteger
+ INI.KeyPhrase := C_KEYPHRASE;
+
+ // Or set INI.Key directly (weaker encryption)
+ // DEBUG: ShowMessageFmt('Key set to %d',[INI.Key]);
+
+ // method: WriteIdent(Const sAuthor,sCopyright,sLicense,sContact:String;Force: boolean=False);
+ // No need to do this each time
+ // Comment this line out once you have the MD5Hash from the ini file
+ if INI.IsVirgin then // DeFlowers
+ begin
+ INI.WriteIdent('Gordon Bamber', '(c)2016', 'LGPL', 'minesadorada@gmail.com', True);
+ // MD5 for this is: 92abf0deecbb25c435bff507a396d92a
+ end
+ else
+ // someone tampered with the ident?
+ if not INI.VerifyIdent('92abf0deecbb25c435bff507a396d92a') then
+ begin
+ ShowMessage('Program ident has been tampered with.' + LineEnding +
+ 'Restoring correct version.');
+ // Last parameter (Optional) forces a rewrite even if FirstRun = 0
+ INI.WriteIdent('Gordon Bamber', '(c)2016', 'LGPL', 'minesadorada@gmail.com', True);
+ end;
+
+ sVersion := C_VERSION;
+ sStoredMD5Hash := '32-character MD5Hash string';
+ INI.ReadSections(cmb_Sections.Items);
+ cmb_Sections.ItemIndex := 0;
+ edt_Section.Text := 'TestSection';
+end;
+
+procedure Tmainform.FormDestroy(Sender: TObject);
+begin
+ If Assigned(INI) then FreeAndNil(INI);
+end;
+
+procedure Tmainform.FormShow(Sender: TObject);
+begin
+ // Test the IsVirgin function
+ if INI.IsVirgin then
+ ShowMessage('First time run of this app');
+end;
+
+procedure Tmainform.mnu_fileCloseClick(Sender: TObject);
+begin
+ Close;
+end;
+
+procedure Tmainform.mnu_helpAboutClick(Sender: TObject);
+// Shows ReadUnencryptedString method
+var
+ s: string;
+begin
+ s := Application.Title + LineEnding;
+ s += 'Version: ' + INI.ReadUnencryptedString('ProgramInfo', IDENT_APPVERSION, '') + LineEnding + LineEnding;
+ s += INI.ReadUnencryptedString('ProgramInfo', IDENT_COPYRIGHT, '');
+ s += ' by ' + INI.ReadUnencryptedString('ProgramInfo', IDENT_AUTHOR, '') + LineEnding;
+ s += 'Licence: ' + INI.ReadUnencryptedString('ProgramInfo', IDENT_LICENSE, '') +
+ LineEnding;
+ s += 'Made with LCL v ' + INI.ReadUnencryptedString('ProgramInfo', IDENT_LCLVERSION, '');
+ s += ' FPC v ' + INI.ReadUnencryptedString('ProgramInfo', IDENT_FPCVERSION, '') +
+ LineEnding;
+ s += 'Compiled ' + INI.ReadUnencryptedString('ProgramInfo', IDENT_LASTCOMPILED, '');
+ s += ' for ' + INI.ReadUnencryptedString('ProgramInfo', IDENT_TARGET, '') +
+ LineEnding;
+ s += 'CryptINI Version: ' + INI.CryptINIVersion + LineEnding;
+ s += 'Cipher in use: ' + INI.CipherType + '. Hash in use: ' + INI.HashType + '.';
+ MessageDlg('About ' + Application.Title, s,
+ mtInformation, [mbOK], 0);
+end;
+
+procedure Tmainform.mnu_helpHelpClick(Sender: TObject);
+begin
+ with ShowINIForm do
+ begin
+ MakeReadOnly;
+ Caption := 'Help for ' + Application.Title;
+ Memo_INI.Lines.Clear;
+ Memo_INI.Lines.Add('This test application is to test the Tcryptini class V ' +
+ INI.CryptINIVersion);
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add(
+ 'The obvious test is to click Write, Read then Verify, which uses automatic values.');
+ Memo_INI.Lines.Add(
+ 'Verification of an entry involves reading the value with its built-in MD5Hash');
+ Memo_INI.Lines.Add(
+ 'and then computing a new MD5Hash from the value, and comparing it with the built-in one.');
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add(
+ 'Additionally, if SectionHashing=TRUE then every time a new entry is added to the section,');
+ Memo_INI.Lines.Add(
+ 'all the entries are combined into a section hash, and it is written/updated as an automatic');
+ Memo_INI.Lines.Add(
+ 'new/updated entry: MD5Hash=<32-character hash> ,which you can use in the VerifySection(MD5Has Value) method');
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add(
+ 'There is a new method WriteSectionValues(Section,Strings) which is absent in TINIFile that you may find useful.');
+ Memo_INI.Lines.Add(
+ 'WriteSectionValues works with PlainText TRUE/FALSE and SectionHashing TRUE/FALSE.');
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add(
+ 'Other new methods are EncryptINI and DecryptINI. This acts on the whole INI file,');
+ Memo_INI.Lines.Add(
+ 'with password, new file extension and auto-deleting the "old" file as optional parameters.');
+ Memo_INI.Lines.Add('The defaults are the INI.KeyPhrase, ".enc" and no auto-deletion.');
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add(
+ 'Wnen property PlainText=TRUE then TCryptINI behaves just as TINIFile did');
+ Memo_INI.Lines.Add(
+ 'This property can be changed on-the-fly to enable a mixed Crypted/Plaintext INI file.');
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add(
+ 'Note that Integer keys have an automatic "' + INTEGER_MARKER +
+ '" added in encrypted mode.');
+ Memo_INI.Lines.Add(
+ 'This is so that the ReadSection method can identify them in Encrypted mode.');
+ Memo_INI.Lines.Add(
+ '(Integers are double-encrypted for extra security, so need to be read differently from all other types)');
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add(
+ 'When testing, use the Show/Edit INI button to see the results. This will help you to understand');
+ Memo_INI.Lines.Add(
+ 'how CryptINI works, and what it can do.');
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add('CryptINI was designed to solve 2 issues:');
+ Memo_INI.Lines.Add(
+ '1. Making sure the ProgramInfo section is never altered, assuring you attribution stays secure.');
+ Memo_INI.Lines.Add(
+ 'Once you have the MD5Hash of it, you can easily make an "authenticity checker" app using CryptINI');
+ Memo_INI.Lines.Add(
+ '2. Storing Passwords and Scores in an editable INI file which is tamper-proof to the casual hacker.');
+ Memo_INI.Lines.Add(
+ '(If numbers are written using WriteInteger, they are especially difficult to alter)');
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add('In this app and ucryptini.pas is a DEFINE: {$DEFINE USE_DCPCRYPT}');
+ Memo_INI.Lines.Add(
+ 'This assumes you have the DCPCrypt runtime/designtime component installed.');
+ Memo_INI.Lines.Add(
+ 'If you haven''t then either install it or comment out the DEFINE and');
+ Memo_INI.Lines.Add(
+ 'remove it from Project Inspector. It is available via OnlinePackageManager');
+ Memo_INI.Lines.Add('');
+ Memo_INI.Lines.Add('Open ucryptini in a text editor to look at the commented code');
+ Memo_INI.Lines.Add('- Enjoy!');
+ ShowModal;
+ end;
+end;
+
+{$IFDEF USE_DCPCRYPT}
+procedure Tmainform.mnu_optionsDecryptINIFileClick(Sender: TObject);
+begin
+ if MessageDlg('Delete encrypted INI file?', mtConfirmation, [mbYes, mbNo], 0, mbYes) =
+ mrYes then
+ INI.DecryptINI(True)
+ else
+ INI.DecryptINI(False);
+ // Remember to reset the key phrase!
+ INI.KeyPhrase := C_KEYPHRASE;
+
+ // Update controls
+ cmb_Sections.Clear;
+ INI.ReadSections(cmb_Sections.Items);
+ cmb_Sections.Refresh;
+ cmb_Sections.ItemIndex := 0;
+end;
+
+procedure Tmainform.mnu_optionsEncryptINIFileClick(Sender: TObject);
+begin
+ if MessageDlg('Delete unencrypted INI file?', mtConfirmation,
+ [mbYes, mbNo], 0, mbYes) = mrYes then
+ INI.EncryptINI(True)
+ else
+ INI.EncryptINI(False);
+ Application.ProcessMessages;
+ // Update controls
+ cmb_Sections.Clear;
+ cmb_Sections.ItemIndex := 0;
+end;
+
+{$ENDIF}
+
+procedure Tmainform.mnu_optionsEncryptionKeyClick(Sender: TObject);
+var
+ s: string;
+ l: longint;
+begin
+ keydialog.sKeyPhrase := INI.KeyPhrase;
+ KeyDialog.ShowModal;
+ if KeyDialog.ModalResult = mrOk then
+ begin
+ s := KeyDialog.edt_key.Text;
+ if TryStrToInt(s, l) then
+ INI.Key := l
+ else
+ INI.Keyphrase := s;
+ ShowMessage('Key changed successfully');
+ end;
+end;
+
+procedure Tmainform.rg_EncryptionSelectionChanged(Sender: TObject);
+begin
+ if rg_Encryption.ItemIndex = 0 then
+ INI.PlainTextMode := False
+ else
+ INI.PlainTextMode := True;
+ if INI.PlainTextMode = True then
+ begin
+ rg_SectionHashing.ItemIndex := 1;
+ INI.SectionHashing := False;
+ end;
+ cmd_Read.Enabled := False;
+ cmd_Verify.Enabled := False;
+end;
+
+procedure Tmainform.rg_SectionHashingSelectionChanged(Sender: TObject);
+begin
+ if rg_SectionHashing.ItemIndex = 0 then
+ INI.SectionHashing := True
+ else
+ INI.SectionHashing := False;
+end;
+
+procedure Tmainform.cmd_WriteClick(Sender: TObject);
+var
+ s: string;
+begin
+ // Best results when writing to TestSection
+ if (edt_Section.Text <> 'TestSection') then
+ if MessageDlg('Please confirm', 'Are you sure you want to write to ' +
+ edt_Section.Text + '? (It should be "TestSection")', mtConfirmation,
+ [mbYes, mbNo], 0, mbNo) = mrNo then
+ begin
+ edt_Section.Text := 'TestSection';
+ ShowMessage('OK. Writing to default section "TestSection" instead');
+ end;
+ INI.SectionHashing := False;
+ INI.WriteString(edt_Section.Text, edt_Ident.Text, edt_Value.Text);
+ INI.WriteInteger(edt_Section.Text, 'Integer', StrToInt(edt_Integer.Text));
+ // Write other types as a test
+ INI.WriteBool(edt_Section.Text, 'Boolean', True);
+ INI.WriteFloat(edt_Section.Text, 'Float', 3.142);
+ INI.WriteDateTime(edt_Section.Text, 'Date', StrToDate('15/10/2016',
+ 'dd mm yyyy', '/'));
+ INI.WriteInt64(edt_Section.Text, 'Int64', 1000);
+ INI.SectionHashing := True;
+ INI.MakeSectionHash(edt_Section.Text, True);
+ cmd_Read.Enabled := True;
+ cmd_Verify.Enabled := True;
+ s := 'Values written:' + LineEnding;
+ s += 'WriteString: ' + edt_Value.Text + LineEnding;
+ s += 'WriteFloat: 3.142' + LineEnding;
+ s += 'WriteDateTime: 15/10/2016' + LineEnding;
+ s += 'WriteInt64: 1000' + LineEnding;
+ s += 'WriteInteger: ' + edt_Integer.Text + LineEnding;
+ INI.ReadSections(cmb_Sections.Items);
+ cmb_Sections.ItemIndex := Pred(cmb_Sections.Items.Count);
+ ShowMessage(s);
+end;
+
+procedure Tmainform.cmd_EraseSectionClick(Sender: TObject);
+begin
+ if not INI.SectionExists(edt_Section.Text) then
+ begin
+ ShowMessage(edt_Section.Text + ' is absent, so nothing to erase!');
+ Exit;
+ end;
+ if edt_Section.Text = IDENT_SECTION then
+ if MessageDlg('Please confirm', 'Are you sure you want to delete ' +
+ IDENT_SECTION + '?', mtConfirmation, [mbCancel, mbYes], 0, mbCancel) =
+ mrCancel then
+ exit;
+ INI.EraseSection(edt_Section.Text);
+ ShowMessage('Section ' + edt_Section.Text + ' is no more.');
+ INI.ReadSections(cmb_Sections.Items);
+ cmb_Sections.ItemIndex := 0;
+end;
+
+procedure Tmainform.cmd_WriteSectionClick(Sender: TObject);
+var
+ iCount: integer;
+ sTempSectionName: string;
+ MyStringList: TStrings;
+begin
+ with InputSectionValuesForm do
+ begin
+ ShowModal;
+ if ModalResult = mrCancel then
+ Exit;
+ sTempSectionName := sSectionName;
+ if sTempSectionName = IDENT_SECTION then
+ if MessageDlg('Please confirm', 'Are you sure you want to write to ' +
+ IDENT_SECTION + '?', mtConfirmation, [mbYes, mbCancel], 0, mbCancel) =
+ mrCancel then
+ exit;
+ edt_Section.Text := sSectionName;
+ MyStringList := TStringList.Create;
+ try
+ MyStringList.Clear;
+ MyStringList.BeginUpdate;
+ for iCount := 0 to (NumberOfControls - 1) do
+ MyStringList.Add(IdentEditArray[iCount].Text + '=' +
+ ValueEditArray[iCount].Text);
+ MyStringList.EndUpdate;
+ INI.WriteSectionValues(sSectionName, MyStringList);
+ INI.UpdateFile;
+ ShowMessage('Section ' + sTempSectionName + ' written successfully');
+ finally
+ MyStringList.Free;
+ end;
+ end;
+ INI.ReadSections(cmb_Sections.Items);
+ cmb_Sections.ItemIndex := Pred(cmb_Sections.Items.Count);
+end;
+
+procedure Tmainform.edt_IntegerEditingDone(Sender: TObject);
+var
+ iTest: longint;
+begin
+ if not TryStrToInt(edt_Integer.Text, iTest) then
+ begin
+ ShowMessageFmt('%s is not an Integer. Resetting to %s',
+ [edt_Integer.Text, '12345']);
+ edt_Integer.Text := '12345';
+ end;
+end;
+
+procedure Tmainform.cmd_ReadClick(Sender: TObject);
+var
+ s: string;
+begin
+ if not INI.SectionExists(edt_Section.Text) then
+ begin
+ ShowMessage(edt_Section.Text + ' is absent, so nothing to read!');
+ Exit;
+ end;
+ // Dont read IDENT_SECTION
+ if edt_Section.Text = IDENT_SECTION then
+ begin
+ cmd_ReadSectionValues.Click;
+ Exit;
+ end;
+ if (edt_Section.Text <> 'TestSection') then
+ if MessageDlg('Please confirm', 'Are you sure you want to read test values from ' +
+ edt_Section.Text + '?', mtConfirmation, [mbCancel, mbYes], 0, mbCancel) =
+ mrCancel then
+ begin
+ edt_Section.Text := 'TestSection';
+ ShowMessage('OK. Reading default section "TestSection" instead');
+ end;
+
+ if INI.PlainTextMode then
+ INI.MD5Hash := 'n/a';
+ s := INI.ReadString(edt_Section.Text, edt_Ident.Text, 'unknown');
+ ShowMessageFmt('Value of %s in %s is %s %s(MD5 hash: %s)',
+ [edt_Ident.Text, edt_Section.Text, s, LineEnding, INI.MD5Hash]);
+ if INI.ReadBool(edt_Section.Text, 'Boolean', False) = True then
+ ShowMessageFmt('ReadBool is TRUE%s(MD5 hash: %s)', [LineEnding, INI.MD5Hash])
+ else
+ ShowMessageFmt('ReadBool is FALSE%s(MD5 hash: %s)', [LineEnding, INI.MD5Hash]);
+ ShowMessageFmt('ReadFloat is %.3f%s(MD5 hash: %s)',
+ [INI.ReadFloat(edt_Section.Text, 'Float', 0), LineEnding, INI.MD5Hash]);
+ ShowMessageFmt('ReadDateTime is %s%s(MD5 hash: %s)',
+ [DateToStr(INI.ReadDateTime(edt_Section.Text, 'Date', NOW)),
+ LineEnding, INI.MD5Hash]);
+ ShowMessageFmt('ReadInt64 is %d%s(MD5 hash: %s)',
+ [INI.ReadInt64(edt_Section.Text, 'Int64', 0), LineEnding, INI.MD5Hash]);
+ ShowMessageFmt('ReadInteger is %d%s(MD5 hash: %s)',
+ [INI.ReadInteger(edt_Section.Text, 'Integer', 0), LineEnding, INI.MD5Hash]);
+end;
+
+procedure Tmainform.cmb_SectionsSelect(Sender: TObject);
+begin
+ edt_Section.Text := cmb_Sections.Items[cmb_Sections.ItemIndex];
+
+end;
+
+procedure Tmainform.cmd_convertToCryptiniClick(Sender: TObject);
+{
+** This routine is a good demonstation of what TCryptINI can do
+** It converts old ini files into encrypted ones.
+
+** Workflow:
+0. Inform user what is about to happen and offer bailout (very important!)
+1. Backup old ini file
+2. Make a working copy
+3. Process the working copy
+4. Seek approval of changes
+5. If yes, Overwrite the old ini file and clean up
+}
+const
+ CR = LineEnding;
+var
+ sINIFilePathToConvert, sSourceINIFilePath, s, TempSectionName, sKeyPhrase: string;
+ sValueEntry, sKey, sValue: string;
+ INIFileToConvert: TCryptINIFile;
+ SectionNameList, ValueList: TStrings;
+ iCount, jCount, lTemp: integer;
+ dtTemp: TDateTime;
+begin
+ try // - EXCEPT
+ s := 'This utility will convert a regular INI file to a CryptINI file' + CR;
+ s += 'using the Password/Keyphrase of your choice.' + CR + CR;
+ s += 'Your chosen INI file will first be backed up in the same folder,' + CR;
+ s += 'and a working copy made. After the conversion you will have a' + CR;
+ s += 'chance to view the changes and either approve or revert them.' + CR + CR;
+ s += 'If you approve, the original INI file will be overwritten by' + CR;
+ s += 'the approved working copy.' + CR;
+ s += 'If you revert, your original INI file will remain intact.' + CR + CR;
+ s += 'Would you like to continue?' + CR;
+ if MessageDlg(s, mtConfirmation, [mbYes, mbNo], 0, mbYes) <> mrYes then
+ Exit;
+ if OpenDialog1.Execute then
+ sINIFilePathToConvert := OpenDialog1.FileName
+ else
+ Exit;
+ // Prevent changing this app's INI file
+ if sINIFilePathToConvert = INI.Filename then
+ begin
+ ShowMessage('You cannot choose the INI file for this application! Try again.');
+ Exit;
+ end;
+ // Make a backup
+ if CopyFile(sINIFilePathToConvert, ChangeFileExt(sINIFilePathToConvert, '.bak')) then
+ ShowMessageFmt('Your existing INI file has been backed up to %s',
+ [ChangeFileExt(sINIFilePathToConvert, '.bak')])
+ else
+ begin
+ ShowMessage('Could not write to ' + ExtractFileDir(sINIFilePathToConvert) +
+ ' - Quitting');
+ Exit;
+ end;
+ // Make a working copy
+ sSourceINIFilePath := ChangeFileExt(sINIFilePathToConvert, '.src');
+ if not CopyFile(sINIFilePathToConvert, sSourceINIFilePath) then
+ begin
+ ShowMessage('Could not write to ' + ExtractFileDir(sINIFilePathToConvert) +
+ ' - Quitting');
+ Exit;
+ end;
+ // Use the working copy
+ INIFileToConvert := TCryptINIFile.Create(sSourceINIFilePath);
+ // Fetch a pass phrase
+ sKeyPhrase := InputBox('Pass Phrase',
+ 'Please type in your pass phrase for this INI file', C_KEYPHRASE);
+ INIFileToConvert.KeyPhrase := sKeyPhrase;
+ // Create temprary stringlists
+ SectionNameList := TStringList.Create;
+ ValueList := TStringList.Create;
+ try
+ // Use as a regular TiniFile
+ INIFileToConvert.PlainTextmode := True;
+ INIFileToConvert.SectionHashing := False;
+ SectionNameList.Clear;
+ // Fetch all the Section names
+ INIFileToConvert.ReadSections(SectionNameList);
+ if SectionNameList.Count > 0 then
+ // For each Sectionnane...
+ for iCount := 0 to Pred(SectionNameList.Count) do
+ begin
+ TempSectionName := SectionNameList[iCount];
+ // Don't process ProgramInfo
+ if TempSectionName = IDENT_SECTION then
+ Continue; // Dont convert this
+ ValueList.Clear;
+ // Fetch all the Key=Values for this Section
+ INIFileToConvert.ReadSectionValues(TempSectionName, ValueList);
+ if ValueList.Count > 0 then
+ // For each Key-Value pair...
+ for jCount := 0 to Pred(ValueList.Count) do
+ begin
+ sValueEntry := ValueList[jCount];
+ sKey := '';
+ sValue := '';
+ // Split into Key and Value
+ If NOT INI.SplitKeyValue(sValueEntry, sKey, sValue) then Continue;
+ // We have the valid key and value else skipped
+ // Don't process MD5Has key
+ if sKey = IDENT_MD5HASH then
+ Continue;
+ // Is it a number?
+ if TryStrToInt(sValue, lTemp) then // Integer
+ begin
+ INIFileToConvert.PlainTextMode := False;
+ // Is it a Boolean?
+ if ((lTemp = 0) or (lTemp = 1)) then // Guess a boolean value?
+ INIFileToConvert.WriteString(TempSectionName, sKey, sValue)
+ else
+ begin
+ // Process Integer Value
+ INIFileToConvert.PlainTextMode := True;
+ // Delete unencrypted key without INTEGER_MARKER
+ INIFileToConvert.DeleteKey(TempSectionName, sKey);
+ INIFileToConvert.PlainTextMode := False;
+ // Rewrite encrypted key with INTEGER_MARKER
+ INIFileToConvert.WriteInteger(TempSectionName, sKey, lTemp);
+ end;
+ INIFileToConvert.PlainTextMode := True;
+ end
+ else // String,Date
+ begin
+ // Process non-numeric values
+ INIFileToConvert.PlainTextMode := False;
+ // Is it a DateTime?
+ if TryStrToDateTime(sValue, dtTemp) then
+ INIFileToConvert.WriteDateTime(TempSectionName, sKey, dtTemp)
+ else
+ // Process String value
+ INIFileToConvert.WriteString(TempSectionName, sKey, sValue);
+ INIFileToConvert.PlainTextMode := True;
+ end;
+ // Hash the whole Section (Make MD5Hash entry)
+ INIFileToConvert.MakeSectionHash(TempSectionName, True);
+ end;
+ end;
+ // Conversion is done. Show the user the Working Copy
+ with ShowINIForm do
+ begin
+ MakeReadOnly; // Put memoform into readonly mode
+ cmd_Abort.Visible := True; // Show the invisible button
+ Caption := 'Contents of converted INI file';
+ Memo_INI.Lines.Clear;
+ // Check the file is still there :)
+ if LazFileUtils.FileExistsUTF8(INIFileToConvert.Filename) then
+ begin
+ Memo_INI.Lines.LoadFromFile(INIFileToConvert.Filename);
+ sINIFilePath := INIFileToConvert.Filename;
+ cmd_Close.Caption := 'Approve conversion';
+ // Display the INI to the user
+ ShowModal;
+ // Tidy up
+ cmd_Close.Caption := '&Close';
+ cmd_Abort.Visible := False; // Make the button invisible again.
+ // What did the user decide?
+ if ModalResult <> mrAbort then // All good - proceed
+ begin
+ INIFileToConvert.UpdateFile;
+ Sleep(100);
+ // Overwrite old INI file:
+ if not CopyFile(sSourceINIFilePath, sINIFilePathToConvert) then
+ begin
+ ShowMessage('Could not Update ' + ExtractFileDir(sINIFilePathToConvert) +
+ ' - Quitting');
+ Exit;
+ end;
+
+{
+ // Encrypt file as well?
+ if MessageDlg(
+ 'Conversion Successful. Would you like to Encrypt the whole file as well?',
+ mtConfirmation, [mbYes, mbNo], 0, mbNo) = mrYes then
+ begin
+ // User wants to encrypt. Get a new pass phrase.
+ sKeyPhrase := InputBox('Pass Phrase',
+ 'Please type in your pass phrase for this INI file', C_KEYPHRASE);
+ // Work with the (changed) original
+ INIFileToConvert.Free; // Finished with the working copy
+ // Load the original
+ INIFileToConvert := TCryptINIFile.Create(sINIFilePathToConvert);
+ INIFileToConvert.KeyPhrase:=sKeyPhrase;
+ // Delete the old INI file or no?
+ if MessageDlg('Delete unencrypted INI file?', mtConfirmation,
+ [mbYes, mbNo], 0, mbYes) = mrYes then
+ INIFileToConvert.EncryptINI(True, sKeyPhrase, '.enc')
+ else
+ INIFileToConvert.EncryptINI(False, sKeyPhrase, '.enc');
+ end;
+ }
+ // All went well - inform the user.
+ ShowMessage('All operations were successful.' + LineEnding +
+ 'Click OK to clean up temporary files');
+ end
+ else
+ // User chose to Revert. sINIFilePathToConvert file is still intact.
+
+ ShowMessage('Conversion aborted. INI file is unchanged.' +
+ LineEnding + 'Click OK to clean up temporary files');
+
+ // Tidy up:
+ // Delete working copy
+ if not DeleteFile(sSourceINIFilePath) then
+ begin
+ ShowMessage('Could not Delete ' + ExtractFileDir(sSourceINIFilePath) +
+ ' - Quitting');
+ Exit;
+ end;
+ // Delete backup file as well?
+ if MessageDlg('Delete backup file?', mtConfirmation, [mbYes, mbNo], 0, mbYes) =
+ mrYes then
+ if not DeleteFile(ChangeFileExt(sINIFilePathToConvert, '.bak')) then
+ begin
+ ShowMessage('Could not Delete ' +
+ ExtractFileDir(ChangeFileExt(sINIFilePathToConvert, '.bak')));
+ Exit;
+ end;
+ end;
+ end;
+ finally
+ ValueList.Free;
+ SectionNameList.Free;
+ FreeAndNil(INIFileToConvert);
+ end;
+ except
+ // It's a long routine. Let's hope we don't get here.
+ On E: Exception do
+ ShowMessageFmt('An error has occurred that is not your fault.%sThe error is%s',
+ [CR, E.Message]);
+ end;
+end;
+
+procedure Tmainform.cmd_DeleteValueClick(Sender: TObject);
+begin
+ if not INI.SectionExists(edt_Section.Text) then
+ begin
+ ShowMessage(edt_Section.Text + ' is absent, so nothing to read!');
+ Exit;
+ end;
+ if (edt_Section.Text <> 'TestSection') then
+ if MessageDlg('Please confirm', 'Are you sure you want to delete test values from ' +
+ edt_Section.Text + '?', mtConfirmation, [mbCancel, mbYes], 0, mbCancel) =
+ mrCancel then
+ begin
+ edt_Section.Text := 'TestSection';
+ ShowMessage('OK. Deleting values in default section "TestSection" instead');
+ end;
+ ShowMessage('Deleting values "' + edt_Ident.Text + '" and "Integer"');
+ INI.DeleteKey(edt_Section.Text, edt_Ident.Text);
+ INI.DeleteKey(edt_Section.Text, 'Integer');
+ if INI.ValueExists(edt_Section.Text, edt_Ident.Text) then
+ ShowMessage(edt_Ident.Text + ' not deleted!')
+ else
+ ShowMessage(edt_Ident.Text + ' deleted');
+ if INI.ValueExists(edt_Section.Text, 'Integer') then
+ ShowMessage('Integer not deleted!')
+ else
+ ShowMessage('Integer deleted');
+
+end;
+
+procedure Tmainform.cmd_ReadSectionValuesClick(Sender: TObject);
+var
+ MyStringList: TStrings;
+ iCount: integer;
+ TempPlainTextMode: boolean;
+begin
+ if not INI.SectionExists(edt_Section.Text) then
+ begin
+ ShowMessage(edt_Section.Text + ' is absent, so nothing to read!');
+ Exit;
+ end;
+ TempPlainTextMode := INI.PlainTextMode;
+ if edt_Section.Text = IDENT_SECTION then
+ INI.PlainTextMode := True;
+ MyStringList := TStringList.Create;
+ try
+ INI.ReadSectionValues(edt_Section.Text, MyStringList);
+ if MyStringList.Count > 0 then
+ for iCount := 0 to Pred(MyStringList.Count) do
+ ShowMessageFmt('Section name: %s%s(Value %d of %d): %s',
+ [edt_Section.Text, LineEnding, iCount + 1, MyStringList.Count,
+ MyStringList[iCount]])
+ else
+ ShowMessage('Nothing in this section!');
+ finally
+ MyStringList.Free;
+ end;
+ INI.PlainTextMode := TempPlainTextMode;
+end;
+
+procedure Tmainform.cmd_ShowINIClick(Sender: TObject);
+var
+ s: string;
+begin
+ with ShowINIForm do
+ begin
+ MakeWriteable;
+ Caption := 'Contents of ' + INI.Filename;
+ Memo_INI.Lines.Clear;
+ {$WARN UNIT_DEPRECATED OFF}
+ if LazFileUtils.FileExistsUTF8(INI.Filename) then
+ begin
+ Memo_INI.Lines.LoadFromFile(INI.Filename);
+ sINIFilePath := INI.Filename;
+ ShowModal;
+ if bDirty then
+ begin
+ s := INI.KeyPhrase;
+ // Reload (with correct keyphrase)
+ INI.Free;
+ {$IFDEF USE_THREADSAFE}
+ INI := TLockCryptIniFile.Create(IniFilePath);
+ INI.Lock;
+ {$ELSE}
+ INI := TCryptIniFile.Create(IniFilePath);
+ {$ENDIF}
+ INI.KeyPhrase := s;
+ end;
+ end
+ else
+ ShowMessage('No INI file to show!');
+ end;
+end;
+
+procedure Tmainform.cmd_ValueExistsClick(Sender: TObject);
+begin
+ if not INI.SectionExists(edt_Section.Text) then
+ begin
+ ShowMessage(edt_Section.Text + ' is absent, so nothing to read!');
+ Exit;
+ end;
+ // Dont read IDENT_SECTION
+ if edt_Section.Text = IDENT_SECTION then
+ begin
+ ShowMessage('Switching Encryption mode off to read ' + IDENT_SECTION + 'section');
+ rg_Encryption.ItemIndex := 1;
+ end;
+ if (edt_Section.Text <> 'TestSection') then
+ if MessageDlg('Please confirm', 'Are you sure you want to test values from ' +
+ edt_Section.Text + '?', mtConfirmation, [mbCancel, mbYes], 0, mbCancel) =
+ mrCancel then
+ begin
+ edt_Section.Text := 'TestSection';
+ ShowMessage('OK. Reading default section "TestSection" instead');
+ end;
+
+ if INI.ValueExists(edt_Section.Text, edt_Ident.Text) then
+ ShowMessage('Key "' + edt_Ident.Text + '" exists')
+ else
+ ShowMessage('Key "' + edt_Ident.Text + '" is absent');
+ if INI.ValueExists(edt_Section.Text, 'Integer') then
+ ShowMessage('Key "Integer" exists')
+ else
+ ShowMessage('Key "Integer" is absent');
+end;
+
+procedure Tmainform.cmd_VerifyClick(Sender: TObject);
+var
+ TRUEFALSE: boolean;
+begin
+ if not INI.SectionExists(edt_Section.Text) then
+ begin
+ ShowMessage(edt_Section.Text + ' is absent, so nothing to verify!');
+ Exit;
+ end;
+ // Dont verify IDENT_SECTION
+ if edt_Section.Text = IDENT_SECTION then
+ begin
+ cmd_VerifySectionValues.Click;
+ Exit;
+ end;
+ if (edt_Section.Text <> 'TestSection') then
+ if MessageDlg('Please confirm', 'Are you sure you want to verify test values from ' +
+ edt_Section.Text + '?', mtConfirmation, [mbCancel, mbYes], 0, mbCancel) =
+ mrCancel then
+ begin
+ edt_Section.Text := 'TestSection';
+ ShowMessage('OK. Verifying default section "TestSection" instead');
+ end;
+ if INI.PlainTextMode = False then
+ begin
+ TRUEFALSE := True; // Assume success, look for failure
+ // Test all the value types one-by-one
+ TRUEFALSE := TRUEFALSE and INI.VerifyBool(edt_Section.Text, 'Boolean', True);
+ if not INI.VerifyBool(edt_Section.Text, 'Boolean', True) then
+ ShowMessage('Boolean failed to verify');
+ TRUEFALSE := TRUEFALSE and INI.VerifyFloat(edt_Section.Text, 'Float', 3.142);
+ if not INI.VerifyFloat(edt_Section.Text, 'Float', 3.142) then
+ ShowMessage('Float failed to verify');
+ TRUEFALSE := TRUEFALSE and INI.VerifyDateTime(edt_Section.Text,
+ 'Date', StrToDate('15/10/2016', 'dd mm yyyy', '/'));
+ if not INI.VerifyDateTime(edt_Section.Text, 'Date',
+ StrToDate('15/10/2016', 'dd mm yyyy', '/')) then
+ ShowMessage('Date failed to verify');
+ TRUEFALSE := TRUEFALSE and INI.VerifyInt64(edt_Section.Text, 'Int64', 1000);
+ if not INI.VerifyInt64(edt_Section.Text, 'Int64', 1000) then
+ ShowMessage('Int64 failed to verify');
+ TRUEFALSE := TRUEFALSE and INI.VerifyString(edt_Section.Text,
+ edt_Ident.Text, edt_Value.Text);
+ if not INI.VerifyString(edt_Section.Text, edt_Ident.Text, edt_Value.Text) then
+ ShowMessage('String failed to verify');
+ if TRUEFALSE = True then
+ ShowMessage('Verify: String,Bool,Float,Date and Int64 types all verified OK')
+ else
+ ShowMessage('One or more types failed verification');
+ // Test the Read/Write/Verify Integer stuff
+ if INI.VerifyInteger(edt_Section.Text, 'Integer', StrToInt(edt_Integer.Text)) then
+ ShowMessage('VerifyInteger: ' + edt_Integer.Text + ' verified OK')
+ else
+ ShowMessage('VerifyInteger: ' + edt_Integer.Text + ' failed verification');
+ end
+ else
+ ShowMessage('Verification of regular values only works when PlainTextMode=FALSE');
+ // Use the MD5 value from the INI file
+ if INI.VerifyIdent('92abf0deecbb25c435bff507a396d92a') then
+ ShowMessage('Ident ' + IDENT_SECTION + ' verified OK')
+ else
+ ShowMessage('Ident ' + IDENT_SECTION + ' failed verification');
+end;
+
+procedure Tmainform.cmd_VerifySectionValuesClick(Sender: TObject);
+var
+ s: string;
+begin
+ if edt_Section.Text = IDENT_SECTION then
+ begin
+ if INI.VerifyIdent('92abf0deecbb25c435bff507a396d92a') then
+ ShowMessage('Ident ' + IDENT_SECTION + ' verified OK')
+ else
+ ShowMessage('Ident ' + IDENT_SECTION + ' failed verification');
+ Exit;
+ end;
+ if not INI.SectionExists(edt_Section.Text) then
+ begin
+ ShowMessage(edt_Section.Text + ' is absent, so nothing to verify!');
+ Exit;
+ end;
+
+ s := InputBox('Verify Section', 'Please enter your 32-character MD5Hash here',
+ sStoredMD5Hash);
+ if INI.VerifySectionHash(edt_Section.Text, s) then
+ begin
+ ShowMessage('Section is verified');
+ sStoredMD5Hash := s;
+ end
+ else
+ ShowMessage('MD5Hash value incorrect. Section failed verification');
+end;
+
+end.
diff --git a/components/cryptini/latest_stable/demo/umemoform.lfm b/components/cryptini/latest_stable/demo/umemoform.lfm
new file mode 100644
index 000000000..9917bdc55
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/umemoform.lfm
@@ -0,0 +1,62 @@
+object ShowINIForm: TShowINIForm
+ Left = 1253
+ Height = 540
+ Top = 313
+ Width = 768
+ BorderStyle = bsToolWindow
+ Caption = 'INI'
+ ClientHeight = 540
+ ClientWidth = 768
+ OnCreate = FormCreate
+ OnShow = FormShow
+ Position = poMainFormCenter
+ LCLVersion = '1.7'
+ object Memo_INI: TMemo
+ Left = 0
+ Height = 482
+ Top = 0
+ Width = 768
+ Align = alTop
+ Lines.Strings = (
+ 'Memo_INI'
+ )
+ OnChange = Memo_INIChange
+ ScrollBars = ssAutoBoth
+ TabOrder = 0
+ WordWrap = False
+ end
+ object cmd_Close: TBitBtn
+ Left = 347
+ Height = 26
+ Top = 500
+ Width = 75
+ AutoSize = True
+ Caption = '&Close'
+ Kind = bkClose
+ ModalResult = 11
+ OnClick = cmd_CloseClick
+ TabOrder = 1
+ end
+ object cmd_saveChanges: TButton
+ Left = 600
+ Height = 25
+ Top = 499
+ Width = 154
+ AutoSize = True
+ Caption = 'Save Changes and Close'
+ Enabled = False
+ OnClick = cmd_saveChangesClick
+ TabOrder = 2
+ end
+ object cmd_Abort: TBitBtn
+ Left = 264
+ Height = 26
+ Top = 500
+ Width = 76
+ Caption = '&Revert'
+ Kind = bkAbort
+ ModalResult = 3
+ TabOrder = 3
+ Visible = False
+ end
+end
diff --git a/components/cryptini/latest_stable/demo/umemoform.pas b/components/cryptini/latest_stable/demo/umemoform.pas
new file mode 100644
index 000000000..4f7a2a5a2
--- /dev/null
+++ b/components/cryptini/latest_stable/demo/umemoform.pas
@@ -0,0 +1,94 @@
+unit umemoform;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Forms, StdCtrls, SysUtils,
+ Buttons, Classes, Dialogs,Controls;
+
+type
+
+ { TShowINIForm }
+
+ TShowINIForm = class(TForm)
+ cmd_Abort: TBitBtn;
+ cmd_saveChanges: TButton;
+ cmd_Close: TBitBtn;
+ Memo_INI: TMemo;
+ procedure cmd_CloseClick(Sender: TObject);
+ procedure cmd_saveChangesClick(Sender: TObject);
+ procedure FormCreate(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ procedure Memo_INIChange(Sender: TObject);
+ private
+ public
+ bDirty: boolean;
+ sINIFilePath: string;
+ // Help = MakeReadonly, OpenINI = MakeWriteable
+ procedure MakeReadOnly;
+ procedure MakeWriteable;
+ end;
+
+var
+ ShowINIForm: TShowINIForm;
+
+implementation
+// Uses umainform;
+{$R *.lfm}
+
+{ TShowINIForm }
+procedure TShowINIForm.MakeReadOnly;
+begin
+ Memo_INI.Readonly:=TRUE;
+end;
+
+procedure TShowINIForm.MakeWriteable;
+begin
+ Memo_INI.Readonly:=FALSE;
+end;
+
+procedure TShowINIForm.cmd_saveChangesClick(Sender: TObject);
+begin
+ if bDirty then
+ try
+ {$I+}
+ Memo_INI.Lines.SaveToFile(sINIFilePath);
+ ShowMessage('Changes saved OK.');
+ Close;
+ except
+ On E: Exception do
+ ShowMessageFmt('Oops! Error: %s', [E.Message]);
+ end;
+end;
+
+procedure TShowINIForm.cmd_CloseClick(Sender: TObject);
+begin
+ if bDirty then
+ If MessageDlg('Discard changes?',mtConfirmation,[MBYES,MBNO],0,MBNO) = mrNo then
+ Begin
+ cmd_saveChanges.Click;
+ Exit;
+ end;
+ Close;
+end;
+
+procedure TShowINIForm.FormCreate(Sender: TObject);
+begin
+ Icon := Application.Icon;
+end;
+
+procedure TShowINIForm.FormShow(Sender: TObject);
+begin
+ bDirty := False;
+ cmd_saveChanges.Enabled := False;
+end;
+
+procedure TShowINIForm.Memo_INIChange(Sender: TObject);
+begin
+ bDirty := True;
+ cmd_saveChanges.Enabled := True;
+end;
+
+end.
diff --git a/components/cryptini/latest_stable/locale/ucryptini.po b/components/cryptini/latest_stable/locale/ucryptini.po
new file mode 100644
index 000000000..e535f25d7
--- /dev/null
+++ b/components/cryptini/latest_stable/locale/ucryptini.po
@@ -0,0 +1,15 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+#: ucryptini.rsnothingwasen
+msgid "Nothing was Entered!"
+msgstr ""
+
+#: ucryptini.rsnotyetinitia
+msgid "Not yet initialised"
+msgstr ""
+
+#: ucryptini.rsunknown
+msgid "unknown"
+msgstr ""
+
diff --git a/components/cryptini/latest_stable/ucryptini.pas b/components/cryptini/latest_stable/ucryptini.pas
new file mode 100644
index 000000000..d9f35c672
--- /dev/null
+++ b/components/cryptini/latest_stable/ucryptini.pas
@@ -0,0 +1,1477 @@
+unit ucryptini;
+{$mode objfpc}{$H+}
+{$DEFINE USE_DCPCRYPT} // Delete this if you don't have the DCrypt library
+ // and CryptINI will use built-in BASE64 instead
+ // (but turn all project debugging checks off!)
+ // DCrypt can be downloaded via OnlinePackageManager
+ // And is vastly preferable to BASE64
+{$IF FPC_VERSION = 3} // inifiles.pp changed in fpc v3.1.? (17 Jan 2016)
+ {$IF FPC_RELEASE > 0}
+ {$IF FPC_PATCH > 0}
+ {$DEFINE FPC311}
+ {$ENDIF}
+ {$ENDIF}
+{$ENDIF}
+
+{ 'Paranoid' version of TIniFile
+
+ Copyright (C) 2016 Gordon Bamber minesadorada@gmail.com
+ Base 64 code by David Barton
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version with the following modification:
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent modules,and
+ to copy and distribute the resulting executable under terms of your choice,
+ provided that you also meet, for each linked independent module, the terms
+ and conditions of the license of that module. An independent module is a
+ module which is not derived from or based on this library. If you modify
+ this library, you may extend this exception to your version of the library,
+ but you are not obligated to do so. If you do not wish to do so, delete this
+ exception statement from your version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
+ for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+PURPOSE
+=======
+This is a TiniFile implementation that is resistant to tampering.
+In normal (PlainTextMode = FALSE) mode, any calls to Write values are
+accompanied by an embedded MD5 hash value (and also reversed then Base64/IDEA Encrypted)
+This is invisible in normal use (i.e. read methods return normal results)
+but there are added methods to internally verify any entries.
+It also is able to write a standard ident section containing various
+details including authorship and copyright. A single function allows
+you to check on app startup whether this section has been altered.
+It also includes a useful 'First Run' functionality.
+It's intended purpose is to store information that cannot be easily altered
+in a text editor (such as HiScores etc) by a weekend scripter.
+The WriteInteger method is the most secure as it double-encrypts as well as
+embedding an MD5Hash value as a checksum. Very handy to save scores etc.
+It is paired with ReadInteger and VerifyInteger
+
+DISCLAIMER
+==========
+This unit does not claim to pass any security tests nor be used in
+any environment where a moderate-level hacker could circumvent it.
+
+ENCRYPTION
+==========
+By Default CryptINI uses the DCPCrypt package for string encryption.
+The mode is IDEA cipher with an MD5 hash for the key. The EncryptINI
+and DecryptINI methods use DCPCrypt RC4 Cipher (With SHA hash)
+There is a $DEFINE USE_DCPCRYPT directive at the top of this file.
+
+If this DEFINE is commented out, then CryptINI will default
+to BASE64 string encryption, which is weaker. Encrypt/DecryptINI methods
+will be unavailable.
+You can then delete any requirement for dcpcrypt in the project inspector.
+
+FREEPASCAL VERSIONS
+===================
+Starting in FPC V3.1.1 there are additional options in TINIFile which
+are implemented in TCryptINI if the FPC Version >= 3.1.1 is detected.
+
+USE AND EXAMPLE CODE
+====================
+** You can hard-code an Ident Section in your INI file
+** and check if it has been altered
+** Typical Form.Create()
+procedure TForm1.FormCreate(Sender: TObject);
+const
+ C_KEYPHRASE = 'I do like to be beside the seaside'; // Move this to the top of the unit
+Begin
+ ...other code
+ // Initialise the encrypted config file
+ INI := TCryptIniFile.Create(ChangeFileExt(Application.EXEName, '.cfg'));
+
+ // First ever run. INI is absent
+ If INI.IsVirgin then INI.WriteIdent('minesadorada','(c)2016','minesadorada@charcodelvalle.com','Creative Commons',TRUE);l
+
+ if NOT INI.VerifyIdent('5b319674f5cb55f3ed1e404e33c25868') then // I got this from the INI file
+ ShowMessage('This is not a genuine copy of ' + Application.Title + '!')
+ else INI.Deflower; // If not Deflowered then the default ini is used.
+
+ // After first run, use the encrypted version
+ If NOT INI.IsVirgin then
+ begin
+ INI.DecryptINI(TRUE,C_KEYPHRASE);
+ // Check the unencrypted version..
+ if NOT INI.VerifyIdent('5b319674f5cb55f3ed1e404e33c25868') then // I got this from the INI file
+ ShowMessage('This is not a genuine copy of ' + Application.Title + '!');
+ end;
+ INI.KeyPhrase:=C_KEYPHRASE; // for subsequent read/writes
+ ...other code
+end;
+
+procedure Tmainform.FormDestroy(Sender: TObject);
+const
+ C_KEYPHRASE = 'I do like to be beside the seaside'; // Move this to the top of the unit
+Begin
+ ...other code
+ INI.EncryptINI(TRUE,C_KEYPHRASE);
+ ...other code
+end;
+
+
+** Has the Ident been tampered with? Put this in Form.Create
+// Use the MD5 value from the INI file (use your own!)
+If INI.VerifyIdent('92abf0deecbb25c435bff507a396d92a') then
+ ShowMessage('Ident verified OK') // do nothing
+else
+ ShowMessage('Ident failed verification'); // Warning message/exit
+
+** Test for first run
+If INI.IsVirgin then // note that doing the test Deflowers the app by default
+ ShowMessage('First time run of this app');
+
+** Toggle to normal UnEncrypted INI use
+(by default it is set to FALSE)
+INI.PlainTextMode:=TRUE;
+INI.WriteString('MySection', 'String', 'MyString'); // just writes normally
+
+** When PlainTextMode = FALSE (default),Write encrypts the value
+ and prefixes the encrypted value with an MD5Hash
+INI.WriteInteger('MySection', 'Integer',1000);
+** Note WriteInteger adds a '+' (INTEGER_MARKER) to the written Key
+ This is so that CryptINI can deal with Integers specially
+ Using CryptINI in either mode, this is invisible in use
+ (i.e. don't add a + to the ident for any methods)
+
+** When PlainTextMode = FALSE (default), use these convenient methods if you like:
+INI.ReadUnencryptedString
+INI.WriteUnencryptedString
+INI.ReadUnEncryptedInteger
+INI.WriteUnencryptedInteger
+
+** Store 'First Run' status using these methods:
+INI.IsVirgin
+INI.Deflower
+INI.ReFlower (useful for testing)
+
+** You can encrypt and decrypt the entire INI file usine EncryptINI and DecryptINI
+ This uses a different cipher and hash method, so is extra-secure
+ cryptINI methods and properties an only work with an unencrypted INI file
+ So; Decrypt on startup, and Encrypt before exit.
+ By default the routines use the KeyPhrase property, but you can override this
+ in the method call along with whether to delete the "old" file and what
+ file extension to use for the encrypted file.
+
+** Original IniFiles methods and properties
+ Just use them as normal. Note: you can mix-and-match Plaintext and Encrypted
+ in the same INI file. Just toggle the property PlainTextMode before Writing/Reading
+}
+
+interface
+
+uses
+ Forms, SysUtils, LazUTF8, LazFileUtils,StrUtils, INIFiles, md5, LCLVersion,
+ Classes,fileinfo,SyncObjs
+ // DEBUGGING for ShowMessage:,Dialogs
+ // ,Dialogs
+ {$IFDEF USE_DCPCRYPT},DCPidea, DCPmd5,DCPrc4, DCPsha1{$ENDIF}
+ ;
+
+
+const
+ // Fixed Ident section values for Read/Write/Verify Ident methods
+ // Change the values here if you like.
+ IDENT_SECTION = 'ProgramInfo';
+ IDENT_APPNAME = 'App name';
+ IDENT_APPVERSION = 'App version';
+ IDENT_EXE = 'App Path';
+ IDENT_AUTHOR = 'Author';
+ IDENT_COPYRIGHT = 'Copyright';
+ IDENT_LICENSE = 'License';
+ IDENT_CONTACT = 'Contact';
+ IDENT_LCLVERSION = 'LCL Version';
+ IDENT_FPCVERSION = 'FPC Version';
+ IDENT_TARGET = 'Target System';
+ IDENT_LASTCOMPILED = 'Last Compiled';
+ IDENT_FIRSTRUN = 'FirstRun';
+ IDENT_MD5HASH = 'MD5Hash';
+
+ // You can change these Consts here if you like
+ INTEGER_MARKER = '+'; // Appended to Crypted Integer Idents
+ // so that ReadSectionValues,ValueExists and DeleteKey methods will work
+ DEFAULT_KEYPHRASE = 'Choose a better keyphrase than this!'; // If none specified
+ {$IFDEF WINDOWS}
+ DEFAULT_EXT = '.enc';
+ {$ELSE}
+ DEFAULT_EXT = 'enc';
+ {$ENDIF}
+
+ {$IFNDEF USE_DCPCRYPT}
+ // Base64 array of bytes. CHANGE AT YOUR PERIL!
+ B64: array[0..63] of byte = (65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 43, 47);
+ {$ENDIF}
+
+C_VERSION = '0.1.2';
+{
+ VERSION HISTORY
+ ===============
+ V 0.0.1: Initial alpha release by (c)2016 minesadorada@charcodelvalle.com (GB)
+ V 0.0.2: Improved Read/Write/Verify integer values (GB)
+ V 0.0.3: Section functions added. Integer marker added. (GB)
+ V 0.0.4: Compatibility test for fpc 3 additions to TIniFiles (GB)
+ V 0.0.5: Added DCPCrypt units and conditional DEFINES. Added Encrypt/Decrypt INI (GB)
+ V 0.0.6: ReadBinaryStream and WriteBinaryStream added (GB)
+ V 0.0.7: ReadSection debugged (GB)
+ V 0.0.8: Bugfix: FPC Version {DEFINE FPC3} corrected (GB)
+ V 0.0.9: Added SplitKeyValue procedure. Other fixes and improvements (GB)
+ V 0.1.0: Added Program Version info to Ident section
+ V 0.1.2: Added thread-safe TLockCryptINIFile class
+ for thread-safe operation (GB)
+ Credit: "Fungus" at http://forum.lazarus.freepascal.org/
+ V 0.1.3: ??
+}
+type
+ TCryptIniFile = class(TIniFile)
+ private
+ // Md5 Stuff
+ fMyDigest: TMDDigest; // defined in md5 unit
+ fMD5String: string;
+ // For Read/write Integers
+ fKey: longint;
+ fKeyPhrase: string;
+ // Encryption on/off
+ fPlainText: boolean;
+ fSectionHashing: boolean;
+ fVersion: string;
+ {$IFDEF USE_DCPCRYPT}
+ DCP_idea1: TDCP_idea;
+ DCP_md5_1: TDCP_md5;
+ {$ENDIF}
+ fHashType:String;
+ fCipherType:String;
+ // Functions to convert values
+ function EncodeString(const sValue: string): string;
+ function DecodeString(const sValue: string): string;
+ // Functions to convert Integers only
+ function EncodeInteger(const iValue: longint): longint;
+ function DecodeInteger(const iValue: longint): longint;
+ // Either..
+ procedure SetKeyPhrase(AValue: string); // Sets fKey from a string (Preferrred method)
+ Procedure SetKey(aValue:Integer); // Also sets Keyphrase (weaker)
+ public
+ {$IFDEF FPC311}
+ constructor Create(const AFileName: string; AOptions: TIniFileOptions = []);
+ override;
+ {$ELSE}
+ constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);override;
+ {$ENDIF}
+ destructor Destroy; override;
+ // Overridden TINIFile methods
+ function ReadInteger(const Section, Ident: string; Default: longint): longint;
+ override;
+ procedure WriteInteger(const Section, Ident: string; Value: longint); override;
+ function ReadString(const Section, Ident, Default: string): string; override;
+ procedure WriteString(const Section, Ident, Value: string); override;
+ procedure ReadSection(const Section: string; Strings: TStrings); override;
+ function ValueExists(const Section, Ident: string): Boolean; override;
+ procedure DeleteKey(const Section, Ident: String); override;
+{$IFDEF FPC311}
+ procedure ReadSectionValues(const Section: string; Strings: TStrings;
+ AOptions: TSectionValuesOptions = []); override;
+{$ELSE}
+ procedure ReadSectionValues(const Section: string; Strings: TStrings); override;
+{$ENDIF}
+ function ReadBinaryStream(const Section, Name: string; Value: TStream): Integer; override;
+ procedure WriteBinaryStream(const Section, Name: string; Value: TStream); override;
+
+ // Added functions
+ // Compares TestValue's value and MD5Hash with the INI file's version
+ function VerifyString(const Section, Ident, TestValue: string): boolean;
+ function VerifyBool(const Section, Ident: string; TestValue: boolean): boolean;
+ function VerifyInt64(const Section, Ident: string; TestValue: int64): boolean;
+ function VerifyFloat(const Section, Ident: string; TestValue: double): boolean;
+ function VerifyDate(const Section, Ident: string; TestValue: TDateTime): boolean;
+ function VerifyTime(const Section, Ident: string; TestValue: TDateTime): boolean;
+ function VerifyDateTime(const Section, Ident: string; TestValue: TDateTime): boolean;
+ // VerifyInteger is more strict
+ function VerifyInteger(const Section, Ident: string; TestValue: longint): boolean;
+
+ // New functionality. Write a whole section in one go.
+ procedure WriteSectionValues(const Section: string; Strings: TStrings);
+ // If SectionHashing = TRUE then MakeSectionHash is called for every write
+ // Best practioe is to do it only after a bunch of writes
+ procedure MakeSectionHash(const Section: string; WriteToINI: boolean = True);
+ function VerifySectionHash(const Section, TestMD5Hash: string): boolean;
+ // Separates key=value into Skey and sValue
+ function SplitKeyValue(Const sValueEntry:String;var sKey,sValue:String):Boolean;
+ // Sets property PlainTextMode to FALSE temporarily to Read/Write normally
+ // I included these functions for simplicity. They work when PlainTextMode=TRUE or FALSE
+ function ReadUnencryptedString(const Section, Ident, Default: string): string;
+ procedure WriteUnencryptedString(const Section, Ident, Value: string);
+ function ReadUnEncryptedInteger(const Section, Ident: string;
+ Default: longint): longint;
+ procedure WriteUnencryptedInteger(const Section, Ident: string; Value: longint);
+
+ // Writes a standard Ident section with MD5 Hash
+ // This can be done in Form.Create() on program first run
+ // Or can be done during development, then distribute the prewritten INI file with the exe
+ procedure WriteIdent(const sAuthor, sCopyright, sLicense, sContact: string; Force: boolean=False);
+ // Read the MD5Hash from the INI file and pass it as a parameter
+ // ANY tampering with the [ProgramInfo] ident section info returns a FALSE
+ // Note not all Key/Values are verified (i.e. Appname etc.)
+ function VerifyIdent(const sMD5Hash: string): boolean;
+
+ {$IFDEF USE_DCPCRYPT}
+ // Encrypt and Decrypt the whole file in one go. Very secure via RC4 cipher.
+ // You can call:
+ // EncryptINI; (use DEFAULT_KEYPHRASE as the passkey, and delete the old file)
+ // EncryptINI(FALSE); (don't delete old ini file)
+ // EncryptINI(TRUE,'bananas'); (delete old file; use "bananas" as the passkey)
+ // EncryptINI(TRUE,'bananas','.xxx'); (delete old file; use bananas as the passkey, and save as an .xxx file)
+ // bSuccess:=EncryptINI(optional parameters); (returns FALSE if anything goes wrong)
+ function EncryptINI(DeleteOld:Boolean = TRUE;sPassword: string = DEFAULT_KEYPHRASE;NewExt:String = DEFAULT_EXT):Boolean;
+ function DecryptINI(DeleteOld:Boolean = TRUE;sPassword: string = DEFAULT_KEYPHRASE;NewExt:String = DEFAULT_EXT):Boolean;
+ {$ENDIF}
+
+ // Use to detect or set 'First Run' status
+ function IsVirgin: boolean;
+ procedure Deflower;
+ procedure Reflower;
+
+ // By Default, PlainTextMode = FALSE (Encrypted mode)
+ property PlainTextMode: boolean read fPlainText write fPlainText;
+ property MD5Hash: string read fMD5String write fMD5String;
+
+ // Either..
+ property KeyPhrase: string read fKeyPhrase write SetKeyPhrase; // also sets Key (preferred property)
+ // or..
+ property Key: longint read fKey write SetKey; // Also sets KeyPhrase (weaker)
+
+ // If SectionHashing=TRUE then a Section hash is written each time any value
+ // is written. This enables VerifySection.
+ property SectionHashing: boolean read fSectionHashing write fSectionHashing;
+
+ property HashType:String read fHashType; // Info
+ property CipherType:String read fCipherType; // Info
+ property CryptINIVersion: string read fVersion; // Info
+ property FileName; //inherited
+{$IFNDEF FPC311}
+ // Reflect changes in fpc 3x
+ property EscapeLineFeeds;
+ Property CaseSensitive;
+ Property StripQuotes;
+{$ENDIF}
+end;
+
+Type
+ TLockCryptINIFile = class(TCryptIniFile)
+ private
+ FLock: TCriticalSection;
+ public
+ destructor Destroy; override;
+ // Call Lock after creation
+ procedure Lock;
+ // Call Unlock before destruction
+ procedure Unlock;
+ end;
+
+resourcestring
+ rsNotYetInitia = 'Not yet initialised';
+ rsNothingWasEn = 'Nothing was Entered!';
+ rsUnknown = 'unknown';
+
+implementation
+
+{TLockCryptINIFile}
+destructor TLockCryptINIFile.Destroy;
+begin
+ FreeAndNil(FLock);
+ inherited;
+end;
+
+procedure TLockCryptINIFile.Lock;
+begin
+ if FLock = nil then FLock:= TCriticalSection.Create;
+ FLock.Acquire;
+end;
+
+procedure TLockCryptINIFile.Unlock;
+begin
+ if FLock <> nil then FLock.Release;
+end;
+
+{TCryptINIFile}
+{$IFDEF FPC311}
+ constructor TCryptIniFile.Create(const AFileName: string;
+ AOptions: TIniFileOptions = []);
+ {
+ TIniFileOption = (ifoStripComments, // Strip comments when reading file
+ ifoStripInvalid, // Strip invalid lines when reading file.
+ ifoEscapeLineFeeds, // Escape linefeeds when reading file.
+ ifoCaseSensitive, // Use Case sensitive section/key names
+ ifoStripQuotes, // Strip quotes when reading string values.
+ ifoFormatSettingsActive, // Use format settings when writing date/float etc.
+ ifoWriteStringBoolean // Write booleans as string
+ );
+ }
+begin
+ // AOptions := AOptions And NOT [ifoWriteStringBoolean];
+ inherited Create(AFileName, AOptions);
+{$ELSE}
+constructor TCryptIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
+begin
+ inherited Create(AFileName,AEscapeLineFeeds);
+{$ENDIF}
+ CacheUpdates := False;
+ fPlainText := False;
+ fSectionHashing := True;
+ fVersion := C_VERSION;
+ fMD5String := rsNotYetInitia;
+ fKey := 300554; // Random default key in case user doesn't set one
+ fKeyPhrase := DEFAULT_KEYPHRASE;
+{$IFDEF USE_DCPCRYPT}
+ DCP_idea1:=TDCP_idea.Create(Nil);
+ DCP_md5_1:=TDCP_md5.Create(Nil);
+ If DCP_idea1.SelfTest then
+ DCP_Idea1.Burn; { Clear all information }
+ DCP_Idea1.InitStr(fKeyPhrase,TDCP_md5);
+ fCipherType:=DCP_Idea1.GetAlgorithm;
+ fHashType:=DCP_md5_1.GetAlgorithm;
+{$ELSE}
+ fHashType:='MD5';
+ fCiperType:='BASE64';
+{$ENDIF}
+end;
+
+// ***************************************************************************//
+
+destructor TCryptIniFile.Destroy;
+begin
+ // Anything to free here?
+ {$IFDEF USE_DCPCRYPT}
+ DCP_idea1.Burn; // Clear key info from memory
+ DCP_idea1.Free;
+ DCP_md5_1.Burn; // Clear key info from memory
+ DCP_md5_1.Free;
+ {$ENDIF}
+ inherited Destroy;
+end;
+
+// ***************************************************************************//
+// Start of internal routines
+// ***************************************************************************//
+{$IFNDEF USE_DCPCRYPT} // Fallback routines
+function B64Encode(pInput: pointer; pOutput: pointer; Size: longint): longint;
+var
+ i, iptr, optr: longint;
+ Input, Output: PByteArray;
+begin
+ Input := PByteArray(pInput);
+ Output := PByteArray(pOutput);
+ iptr := 0;
+ optr := 0;
+ for i := 1 to (Size div 3) do
+ begin
+ Output^[optr + 0] := B64[Input^[iptr] shr 2];
+ Output^[optr + 1] := B64[((Input^[iptr] and 3) shl 4) + (Input^[iptr + 1] shr 4)];
+ Output^[optr + 2] := B64[((Input^[iptr + 1] and 15) shl 2) + (Input^[iptr + 2] shr 6)];
+ Output^[optr + 3] := B64[Input^[iptr + 2] and 63];
+ Inc(optr, 4);
+ Inc(iptr, 3);
+ end;
+ case (Size mod 3) of
+ 1:
+ begin
+ Output^[optr + 0] := B64[Input^[iptr] shr 2];
+ Output^[optr + 1] := B64[(Input^[iptr] and 3) shl 4];
+ Output^[optr + 2] := byte('=');
+ Output^[optr + 3] := byte('=');
+ end;
+ 2:
+ begin
+ Output^[optr + 0] := B64[Input^[iptr] shr 2];
+ Output^[optr + 1] := B64[((Input^[iptr] and 3) shl 4) + (Input^[iptr + 1] shr 4)];
+ Output^[optr + 2] := B64[(Input^[iptr + 1] and 15) shl 2];
+ Output^[optr + 3] := byte('=');
+ end;
+ end;
+ Result := ((Size + 2) div 3) * 4;
+end;
+
+// ***************************************************************************//
+
+function Base64Encode(const Value: ansistring): ansistring;
+begin
+ SetLength(Result, ((Length(Value) + 2) div 3) * 4);
+ B64Encode(@Value[1], @Result[1], Length(Value));
+end;
+
+// ***************************************************************************//
+
+function B64Decode(pInput: pointer; pOutput: pointer; Size: longint): longint;
+var
+ i, j, iptr, optr: longint;
+ Temp: array[0..3] of byte;
+ Input, Output: PByteArray;
+begin
+ Input := PByteArray(pInput);
+ Output := PByteArray(pOutput);
+ iptr := 0;
+ optr := 0;
+ Result := 0;
+ for i := 1 to (Size div 4) do
+ begin
+ for j := 0 to 3 do
+ begin
+ case Input^[iptr] of
+ 65..90: Temp[j] := Input^[iptr] - Ord('A');
+ 97..122: Temp[j] := Input^[iptr] - Ord('a') + 26;
+ 48..57: Temp[j] := Input^[iptr] - Ord('0') + 52;
+ 43: Temp[j] := 62;
+ 47: Temp[j] := 63;
+ 61: Temp[j] := $FF;
+ end;
+ Inc(iptr);
+ end;
+ Output^[optr] := (Temp[0] shl 2) or (Temp[1] shr 4);
+ Result := optr + 1;
+ if (Temp[2] <> $FF) and (Temp[3] = $FF) then
+ begin
+ Output^[optr + 1] := (Temp[1] shl 4) or (Temp[2] shr 2);
+ Result := optr + 2;
+ Inc(optr);
+ end
+ else if (Temp[2] <> $FF) then
+ begin
+ try
+ Output^[optr + 1] := (Temp[1] shl 4) or (Temp[2] shr 2);
+ Output^[optr + 2] := (Temp[2] shl 6) or Temp[3]; // Throws error
+ Result := optr + 3;
+ Inc(optr, 2);
+ except
+ On E: Exception do
+ Continue;
+ end;
+ end;
+ Inc(optr);
+ end;
+end;
+
+// ***************************************************************************//
+
+function Base64Decode(const Value: ansistring): string;
+begin
+ SetLength(Result, (Length(Value) div 4) * 3);
+ SetLength(Result, B64Decode(@Value[1], @Result[1], Length(Value)));
+end;
+{$ENDIF}
+// ***************************************************************************//
+
+function CharToBool(AChar: char): boolean;
+begin
+ Result := (Achar = '1');
+end;
+
+// ***************************************************************************//
+
+function BoolToChar(ABool: boolean): char;
+begin
+ if ABool then
+ Result := '1'
+ else
+ Result := '0';
+end;
+
+// ***************************************************************************//
+// End of internal routines
+// ***************************************************************************//
+
+Procedure TCryptIniFile.SetKey(aValue:Integer); // Also sets KeyPhrase
+begin
+ fKey:=AValue;
+ {$IFNDEF USE_DCPCRYPT}
+ SetKeyPhrase(fKey); // VERY WEAK!
+ {$ENDIF}
+end;
+
+// ***************************************************************************//
+
+procedure TCryptIniFile.SetKeyPhrase(AValue: string);
+// Hash the phrase to a standard 32 chars
+// Then sum the ASCII values to seed the fKey (it never exceeds LongInt)
+// Difficult to reverse-engineer even if you have the KeyPhrase
+var
+ aDigest: TMDDigest; // defined in md5 unit
+ iCount: integer;
+ lTry: longint;
+begin
+ {$IFDEF USE_DCPCRYPT}
+ // Use the string directly
+ fKeyPhrase:=AValue;
+ if fKeyPhrase = '' then
+ fKeyPhrase := DEFAULT_KEYPHRASE; // don't allow empty string
+ DCP_Idea1.Burn; { Clear all stored key information }
+ DCP_Idea1.InitStr(fKeyPhrase,TDCP_md5);
+ Exit; // bail out
+ {$ENDIF}
+ lTry := 0;
+ // If it's already a number, just use it. (unless it's zero!)
+ if ((TryStrToInt(AValue, lTry)) and (lTry <> 0)) then
+ fKey := lTry
+ else // It's a string
+ begin
+ if AValue = '' then
+ AValue := rsNothingWasEn; // don't allow empty string
+ aDigest := MDString(AValue, MD_VERSION_5);
+ fKeyPhrase := MDPrint(aDigest); // 32 chars
+ // Mess it up to make a big enough number (96 chars)
+ fKeyPhrase := fKeyPhrase + ReverseString(fKeyPhrase) + fKeyPhrase;
+ fKey := 0;
+ for iCount := 1 to (Length(fKeyPhrase)) do
+ begin
+ fKey := fKey + Ord(fKeyPhrase[iCount]);
+ end;
+ // Make the number bigger
+ fKey := fKey * 7;
+ end;
+end;
+
+
+// ***************************************************************************//
+
+function TCryptIniFile.EncodeString(const sValue: string): string;
+ // Muddle the String up and encode it in IDEA or Base64
+var
+ s: string;
+begin
+ s := ReverseString(sValue);
+ {$IFDEF USE_DCPCRYPT}
+ DCP_Idea1.Reset;{ Reset any stored chaining information }
+ Result:=DCP_Idea1.EncryptString(s);
+ {$ELSE}
+ Result := Base64Encode(s);
+ {$ENDIF}
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.DecodeString(const sValue: string): string;
+ // Decode the IDEA or Base64 then Unmuddle
+var
+ s: string;
+begin
+ {$IFDEF USE_DCPCRYPT}
+ DCP_Idea1.Reset;{ Reset any stored chaining information }
+ s:=DCP_Idea1.DecryptString(sValue);
+ {$ELSE}
+ s := Base64Decode(sValue);
+ {$ENDIF}
+ Result := ReverseString(s);
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.EncodeInteger(const iValue: longint): longint;
+begin
+ //DEBUG: ShowMessageFmt('EncodeInteger value = %d',[iValue]);
+ // B64Encode(@iValue,@Result,SizeOf(iValue)); deprecated
+ Result := iValue xor fKey;
+ // Already IDEA or BASE64 encoded after writing
+ //DEBUG: ShowMessageFmt('EncodeInteger Result = %d',[Result]);
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.DecodeInteger(const iValue: longint): longint;
+begin
+ // B64Decode(@iValue,@Result,SizeOf(iValue)); deprecated
+ // Already IDEA or BASE64 decoded before reading
+ Result := iValue xor fKey;
+end;
+
+// ***************************************************************************//
+
+procedure TCryptIniFile.Deflower;
+// Called by IsVirgin to set FirstRun status to FALSE
+var
+ tempPlainText: boolean;
+begin
+ tempPlainText := fPlainText;
+ fPlainText := True;
+ WriteBool(IDENT_SECTION, IDENT_FIRSTRUN, False);
+ fPlainText := tempPlainText;
+end;
+
+// ***************************************************************************//
+
+procedure TCryptIniFile.Reflower;
+// Set FirstRun status to TRUE
+var
+ tempPlainText: boolean;
+begin
+ tempPlainText := fPlainText;
+ fPlainText := True;
+ WriteBool(IDENT_SECTION, IDENT_FIRSTRUN, True);
+ fPlainText := tempPlainText;
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.IsVirgin: boolean;
+ // On FirstRun returns TRUE
+ // Then calls Deflower so subsequent runs will return FALSE
+var
+ tempPlainText: boolean;
+begin
+ tempPlainText := fPlainText;
+ fPlainText := True;
+ Result := ReadBool(IDENT_SECTION, IDENT_FIRSTRUN, True);
+ if Result = True then
+ Deflower;
+ fPlainText := tempPlainText;
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.VerifyIdent(const sMD5Hash: string): boolean;
+ // The sMD5Hash has been previously generated by WriteIdent
+ // and stored in INI file. Get it from there.
+var
+ s: string;
+ tempPlainText: boolean;
+begin
+ Result := False;
+ tempPlainText := fPlainText; // Store old value
+ fPlainText := True; // Set PlainTextMode ON
+ // Make a string of all the authorship entries
+ s := ReadString(IDENT_SECTION, IDENT_AUTHOR, rsUnknown);
+ s := s + ReadString(IDENT_SECTION, IDENT_COPYRIGHT, rsUnknown);
+ s := s + ReadString(IDENT_SECTION, IDENT_LICENSE, rsUnknown);
+ s := s + ReadString(IDENT_SECTION, IDENT_CONTACT, rsUnknown);
+ // Make up an MD5 hash from the INI values read
+ fMyDigest := MDString(s, MD_VERSION_5);
+ fMD5String := MDPrint(fMyDigest);
+ // Compare it with the value passed in the parameter
+ if fMD5String = sMD5Hash then
+ Result := True;
+ fPlainText := tempPlainText; // Restore old value
+end;
+
+// ***************************************************************************//
+
+procedure TCryptIniFile.WriteIdent(const sAuthor, sCopyright, sLicense, sContact: string; Force: boolean=False);
+// This proc will only write anything if IsVirgin (First Run status) is TRUE
+// Or the INI file is empty or altered
+var
+ s: string;
+ tempPlainText: boolean;
+ VInfo:TFileVersionInfo;
+ Version:TProgramVersion;
+begin
+ tempPlainText := fPlainText; // Store old value
+ fPlainText := True; // Set PlainTextMode ON
+ if ((ReadBool(IDENT_SECTION, IDENT_FIRSTRUN, True) = True) OR (Force = True)) then
+ begin // IsVirgin=TRUE so write the section in plaintext
+ WriteString(IDENT_SECTION, IDENT_AUTHOR, sAuthor);
+ WriteString(IDENT_SECTION, IDENT_COPYRIGHT, sCopyright);
+ WriteString(IDENT_SECTION, IDENT_LICENSE, sLicense);
+ WriteString(IDENT_SECTION, IDENT_CONTACT, sContact);
+ WriteString(IDENT_SECTION, IDENT_LCLVERSION, lcl_version);
+ WriteString(IDENT_SECTION, IDENT_FPCVERSION, {$I %FPCVERSION%});
+ s:={$I %FPCTARGETCPU%}+' - '+{$I %FPCTARGETOS%};
+ WriteString(IDENT_SECTION, IDENT_TARGET, s);
+ // If you don't need this you can delete 'Forms' from the Uses clause
+ WriteString(IDENT_SECTION, IDENT_APPNAME,Application.Title);
+ s:='0.0.0.0'; // Default
+ VInfo:=TFileVersionInfo.Create(Application);
+ TRY
+ VInfo.ReadFileInfo;
+ If fileinfo.GetProgramVersion(Version) then
+ s:=ProgramversionToStr(Version);
+ finally
+ VInfo.free;
+ end;
+ WriteString(IDENT_SECTION,IDENT_APPVERSION,s);
+ WriteString(IDENT_SECTION, IDENT_EXE, ParamStrUTF8(0));
+ WriteBool(IDENT_SECTION,IDENT_FIRSTRUN,TRUE);
+ s:={$I %DATE%} + ' at ' + {$I %TIME%};
+ WriteString(IDENT_SECTION, IDENT_LASTCOMPILED, s);
+ // Make up the MD5Hash of all the authorship entries
+ s := sAuthor + sCopyright + sLicense + sContact;
+ fMyDigest := MDString(s, MD_VERSION_5);
+ fMD5String := MDPrint(fMyDigest);
+ WriteString(IDENT_SECTION, IDENT_MD5HASH, fMD5String);
+ UpdateFile;
+ end;
+ fPlainText := tempPlainText; // Restore old value
+end;
+
+// ***************************************************************************//
+
+procedure TCryptIniFile.WriteUnencryptedString(const Section, Ident, Value: string);
+var
+ tempPlainText: boolean;
+ // Temporarily sets PlainTextMode = TRUE then restores previous status
+begin
+ tempPlainText := fPlainText; // Store old value
+ fPlainText := True; // Set PlainTextMode ON
+ WriteString(Section, Ident, Value);
+ fPlainText := tempPlainText; // Restore old value
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.ReadUnencryptedString(
+ const Section, Ident, Default: string): string;
+ // Temporarily sets PlainTextMode = TRUE then restores previous status
+var
+ tempPlainText: boolean;
+begin
+ tempPlainText := fPlainText; // Store old value
+ fPlainText := True; // Set PlainTextMode ON
+ Result := ReadString(Section, Ident, Default);
+ fPlainText := tempPlainText; // Restore old value
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.ReadUnEncryptedInteger(const Section, Ident: string;
+ Default: longint): longint;
+ // Temporarily sets PlainTextMode = TRUE then restores previous status
+var
+ tempPlainText: boolean;
+begin
+ tempPlainText := fPlainText; // Store old value
+ fPlainText := True; // Set PlainTextMode ON
+ Result := ReadInteger(Section, Ident, Default);
+ fPlainText := tempPlainText; // Restore old value
+end;
+
+// ***************************************************************************//
+
+procedure TCryptIniFile.WriteUnencryptedInteger(const Section, Ident: string;
+ Value: longint);
+// Temporarily sets PlainTextMode = TRUE then restores previous status
+var
+ tempPlainText: boolean;
+begin
+ tempPlainText := fPlainText; // Store old value
+ fPlainText := True; // Set PlainTextMode ON
+ WriteInteger(Section, Ident, Value);
+ fPlainText := tempPlainText; // Restore old value
+end;
+
+(******************************************************************************)
+// Subclass of TIniFiles to enable obfuscation of entries
+(******************************************************************************)
+function TCryptIniFile.ReadInteger(const Section, Ident: string;
+ Default: longint): longint;
+ // Unobfuscates the number if PlainTextMode=FALSE
+var
+ s: string;
+ iTemp: longint;
+begin
+ if fPlainText = True then
+ begin
+ // Read result using TiniFile
+ Result := inherited ReadInteger(Section, Ident, Default);
+ Exit;
+ end;
+ Result := Default; // If all else fails..
+ s := ReadString(Section, Ident + INTEGER_MARKER, IntToStr(Default));
+ //DEBUG: ShowMessage(s);
+ if s = IntToStr(Default) then
+ Exit // nothing there - return Default value
+ else
+ begin
+ if not TryStrToInt(s, iTemp) then
+ Exit; // Error - return Default value
+ Result := DecodeInteger(iTemp); // Un-Scramble it
+ end;
+end;
+
+(******************************************************************************)
+
+procedure TCryptIniFile.WriteInteger(const Section, Ident: string; Value: longint);
+// Obfuscates the number if PlainTextMode=FALSE
+begin
+ if fPlainText = False then
+ begin
+ Value := EncodeInteger(Value); // Scramble it
+ inherited WriteInteger(Section, Ident + INTEGER_MARKER, Value); // Write+ using TiniFile
+ end
+ else inherited WriteInteger(Section, Ident, Value); // Write using TiniFile
+ if SectionHashing = True then
+ MakeSectionHash(Section, True);
+ UpdateFile;
+end;
+
+(******************************************************************************)
+
+function TCryptIniFile.ReadString(const Section, Ident, Default: string): string;
+ // Behaviour depends on PlainTextMode = FALSE/TRUE
+var
+ s: string;
+begin
+ fMD5String:='';
+ // Read value via TIniFile
+ s := inherited ReadString(Section, Ident, Default);
+ Result := s;
+ if fPlainText = True then // Use unencrypted version
+ Exit;
+ If (Length(s) < 32) then Exit;
+
+ fMD5String := LeftStr(s, 32); // Grab the MD5 string and store (unencoded)
+ Result := RightStr(s, Length(s) - 32); // Use the rest
+ if Result <> EncodeString(Default) then
+ begin
+ Result := DecodeString(Result); // Un-Encrypt it
+ end;
+end;
+
+(******************************************************************************)
+
+function TCryptIniFile.ValueExists(const Section, Ident: string): Boolean;
+// In Encrypted mode Integers have a special marker
+Var s:String;
+begin
+ Result:= inherited ValueExists(Section,Ident); //Normal result
+ if fPlainText = False then
+ begin
+ If Ident = '' then // bail out
+ begin
+ Result:=FALSE;
+ exit;
+ end;
+ // Test against both Ident and (Ident + INTEGER_MARKER)
+ s:=Ident;
+ s := s + INTEGER_MARKER; // Add INTEGER_MARKER
+ Result:=Result OR inherited ValueExists(Section,s);
+ end;
+end;
+
+(******************************************************************************)
+
+procedure TCryptIniFile.DeleteKey(const Section, Ident: String);
+// In Encrypted mode Integers have a special marker
+Var s:String;
+begin
+ If Ident = '' then exit; // Bail out
+ inherited DeleteKey(Section,Ident); //Normal behaviour
+ // Ident might have an INTEGER_MARKER so preceding code didn't delete it
+ // So delete (Ident + INTEGER_MARKER) if its there
+ s:=Ident; // Make a copy
+ // DEBUG: ShowMessage(s);
+ s := s + INTEGER_MARKER; // Add INTEGER_MARKER
+ inherited DeleteKey(Section,s);
+ If SectionHashing then MakeSectionHash(Section,TRUE);
+ UpdateFile;
+end;
+
+(******************************************************************************)
+
+ procedure TCryptIniFile.WriteString(const Section, Ident, Value: string);
+// Writes MD5Digest string + BASE64 or IDEA string
+// Behaviour depends on PlainTextMode = FALSE/TRUE
+var
+ s: string;
+begin
+ if fPlainText = True then // Write regular string
+ begin
+ // Use regular TiniFile
+ inherited WriteString(Section, Ident, Value);
+ UpdateFile;
+ Exit;
+ end;
+ s := EncodeString(Value); // Encode the string (IDEA/BASE64)
+ fMyDigest := MDString(s, MD_VERSION_5);
+ fMD5String := MDPrint(fMyDigest); // Get the MD5Hash of the encoded string
+ s := fMD5String + s; // Prefix it to the encoded string
+ inherited WriteString(Section, Ident, s); // Write the result using TiniFile
+ if SectionHashing = True then
+ MakeSectionHash(Section, True);
+ UpdateFile;
+end;
+
+(******************************************************************************)
+
+Function TCryptIniFile.SplitKeyValue(Const sValueEntry:String;var sKey,sValue:String):Boolean;
+Var iEqualsPos:Integer;
+begin
+ Result:=FALSE;
+ If Length(sValueEntry) < 4 then exit;
+ iEqualsPos := Pos('=', sValueEntry);
+ if (iEqualsPos >= 2) then
+ begin
+ sKey := LeftStr(sValueEntry, iEqualsPos - 1);
+ sValue := RightStr(sValueEntry, Length(sValueEntry) - iEqualsPos);
+ Result:=TRUE;
+ end;
+end;
+
+(******************************************************************************)
+
+procedure TCryptIniFile.ReadSection(const Section: string; Strings: TStrings);
+// If PlainText = FALSE then
+// 1. Remove any INTEGER_MARKER
+// 2. Remove MD5Hash entry
+Var
+ TempStrings:TStrings;
+ iCount,iMD5HashEntry: integer;
+ s: string;
+begin
+ inherited ReadSection(Section, Strings); // Use regular TiniFile
+ if fPlainText = True then exit;// Return faithful copy
+
+ // Deal with PlainText = FALSE
+ iMD5HashEntry := -1; // dummy value
+ TempStrings := TStringList.Create; // Use a copy
+ TempStrings.BeginUpdate;
+ try
+ // Make a Copy
+ TempStrings.Clear;
+ TempStrings.AddStrings(Strings); // contains encrypted versions
+ for iCount := 0 to Pred(TempStrings.Count) do
+ begin
+ // Do not retrieve MD5Hash entry
+ if (Pos(IDENT_MD5HASH, TempStrings[iCount]) = 0) then
+ begin
+ s := TempStrings[iCount];
+ If RightStr(s,1) = INTEGER_MARKER then
+ TempStrings[iCount]:=LeftStr(s,Length(s)-1);
+ end
+ else
+ iMD5HashEntry := iCount;
+ end;
+ finally
+ TempStrings.EndUpdate;
+ // Copy decoded versions back into the calling StringList
+ if (iMD5HashEntry >= 0) then
+ TempStrings.Delete(iMD5HashEntry); // Dont return the MD5Hash entry
+ Strings.Clear;
+ Strings.AddStrings(TempStrings);
+ TempStrings.Free;
+ end;
+end;
+
+procedure TCryptIniFile.WriteSectionValues(const Section: string; Strings: TStrings);
+// Writes either normal or Encrypted values depending on property PlainTextMode
+// If SectionHashing = TRUE, also writes the MD5Hash entry at the end (for VerifySection method)
+var
+ iCount, iTemp: integer;
+ s, sKey, sValue: string;
+ f: double;
+ TempSectionHashing: boolean;
+begin
+ TempSectionHashing := fSectionHashing; // Preserve property
+ fSectionHashing := False; // Write section without on-the-fly hashing
+ for iCount := 0 to Strings.Count - 1 do
+ begin
+ s := Strings[iCount];
+ // Split key and value
+ If NOT SplitKeyValue(s,sKey,sValue) then continue;
+ if TryStrToInt(sValue, iTemp) = True then
+ begin
+ WriteInteger(Section, sKey, iTemp);
+ Continue;
+ end;
+ if ((LowerCase(sValue) = 'true') or (LowerCase(sValue) = 'false')) then
+ begin
+ if (LowerCase(sValue) = 'true') then
+ WriteBool(Section, sKey, True)
+ else
+ WriteBool(Section, sKey, False);
+ Continue;
+ end;
+ if TryStrToFloat(sValue, f) = True then
+ begin
+ WriteFloat(Section, sKey, f);
+ Continue;
+ end;
+ // All other values
+ WriteString(Section, sKey, sValue);
+ end;
+ // Now is the time to write the MD5Hash entry
+ if TempSectionHashing = True then
+ begin
+ fSectionHashing := True;
+ MakeSectionHash(Section, True);
+ end;
+ UpdateFile;
+ fSectionHashing := TempSectionHashing; // Restore property
+end;
+
+// ***************************************************************************//
+{$IFDEF FPC311}
+ // TSectionValuesOption = (svoIncludeComments,svoIncludeInvalid, svoIncludeQuotes);
+ procedure TCryptIniFile.ReadSectionValues(const Section: string;
+ Strings: TStrings; AOptions: TSectionValuesOptions = []);
+{$ELSE}
+ procedure TCryptIniFile.ReadSectionValues(const Section: string; Strings: TStrings);
+{$ENDIF}
+var
+ TempStrings: TStrings;
+ iCount, iMD5HashEntry: integer;
+ s, sKey, sValue: string;
+ iTemp: longint;
+begin
+{$IFDEF FPC311}
+ inherited ReadSectionValues(Section, Strings, AOptions);
+{$ELSE}
+ inherited ReadSectionValues(Section, Strings);
+{$ENDIF}
+ if fPlainText = True then // Use regular method
+ begin
+ // Use regular TiniFile
+ UpdateFile;
+ Exit;
+ end;
+ iMD5HashEntry := -1; // dummy value
+ TempStrings := TStringList.Create; // Use a copy
+ TempStrings.BeginUpdate;
+ try
+ // Make a Copy
+ TempStrings.Clear;
+ TempStrings.AddStrings(Strings); // contains encrypted versions
+ // Iterate through the list
+ for iCount := 0 to Pred(TempStrings.Count) do
+ begin
+ // Do not retrieve MD5Hash entry
+ if (Pos(IDENT_MD5HASH, TempStrings[iCount]) = 0) then
+ begin
+ s := TempStrings[iCount];
+ // Split key and value
+ If NOT SplitKeyValue(s,sKey,sValue) then Exit;
+ // Deal with Integers, which are IDEA or BASE64 encoded then Encrypted
+ if RightStr(sKey, 1) = INTEGER_MARKER then // It's an Integer value
+ begin
+ s := DecodeString(RightStr(sValue, Length(sValue) - 32));
+ // Decode (IDEA/BASE64) to a string
+ if TryStrToInt(s, iTemp) then
+ begin
+ iTemp := DecodeInteger(iTemp); // Decrypt the integer
+ s := IntToStr(iTemp); // String value of decoded integer
+ end;
+ sKey := LeftStr(sKey, Length(sKey) - 1); // Trim off the INTEGER_MARKER
+ end
+ else
+ begin
+ // All but Integers are simply IDEA or BASE64 encoded
+ s := DecodeString(RightStr(sValue, Length(sValue) - 32));
+ end;
+ // Replace encoded version with decoded version
+ TempStrings[iCount] := sKey + '=' + s;
+ end
+ else
+ iMD5HashEntry := iCount;
+ // Grab the sKey and sValue (we only want to decrypt the Value)
+ end;
+ finally
+ TempStrings.EndUpdate;
+ // Copy decoded versions back into the calling StringList
+ if (iMD5HashEntry >= 0) then
+ TempStrings.Delete(iMD5HashEntry); // Dont return the MD5Hash entry
+ Strings.Clear;
+ Strings.AddStrings(TempStrings);
+ TempStrings.Free;
+ end;
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.ReadBinaryStream(const Section, Name: string; Value: TStream): Integer;
+// Force normal TINIFile mode
+Var
+ tempPlainTextMode:Boolean;
+ tempSectionHashing:Boolean;
+begin
+ tempPlainTextMode:=PlainTextMode;
+ tempSectionHashing:=SectionHashing;
+ PlainTextMode:=TRUE;
+ SectionHashing:=FALSE;
+ Result:=inherited ReadBinaryStream(Section, Name,Value);
+ PlainTextMode:=tempPlainTextMode;
+ SectionHashing:=tempSectionHashing;
+end;
+
+// ***************************************************************************//
+
+procedure TCryptIniFile.WriteBinaryStream(const Section, Name: string; Value: TStream);
+// Force normal TINIFile mode
+Var
+ tempPlainTextMode:Boolean;
+ tempSectionHashing:Boolean;
+begin
+ tempPlainTextMode:=PlainTextMode;
+ tempSectionHashing:=SectionHashing;
+ PlainTextMode:=TRUE;
+ SectionHashing:=FALSE;
+ inherited WriteBinaryStream(Section, Name,Value);
+ PlainTextMode:=tempPlainTextMode;
+ SectionHashing:=tempSectionHashing;
+end;
+
+// ***************************************************************************//
+// ***************************************************************************//
+
+ procedure TCryptIniFile.MakeSectionHash(const Section: string; WriteToINI: boolean);
+// VerifySection uses WriteToINI=FALSE
+var
+ TempStrings: TStrings;
+ iCount: integer;
+ s: string;
+ tempPlainText: boolean;
+begin
+ if fSectionHashing = False then Exit;
+ tempPlainText:=fPlainText; // Store value
+ TempStrings := TStringList.Create;
+ TempStrings.BeginUpdate;
+ try
+ s := '';
+ ReadSectionValues(Section, TempStrings);
+ for iCount := 0 to Pred(TempStrings.Count) do
+ // Ignore the MD5Hash entry
+ if (Pos(IDENT_MD5HASH, TempStrings[iCount]) = 0) then
+ s := s + TempStrings[iCount];
+ //DEBUG: else ShowMessage('Found ' + IDENT_MD5HASH);
+ fMyDigest := MDString(s, MD_VERSION_5);
+ fMD5String := MDPrint(fMyDigest);
+ //DEBUG: ShowMessage(fMD5String);
+ TempPlainText := fPlainText; // Store old value
+ fPlainText := True; // Set PlainTextMode ON
+ // Default WriteToINI = TRUE
+ if WriteToINI then
+ WriteString(Section, IDENT_MD5HASH, fMD5String);
+ fPlainText := tempPlainText; // Restore old value
+ finally
+ TempStrings.EndUpdate;
+ TempStrings.Free;
+ end;
+end;
+
+function TCryptIniFile.VerifySectionHash(const Section, TestMD5Hash: string): boolean;
+begin
+ Result := False;
+ if fSectionHashing = False then Exit;
+ {
+ if ((fSectionHashing = False) or (fPlainText = True)) then
+ Exit;
+ }
+ fMD5String := '';
+ // Rehash, but without writing in order to set fMD5String correctly
+ MakeSectionHash(Section, False);
+ //DEBUG: ShowMessage(fMD5String);
+ //DEBUG: ShowMessage(TestMD5Hash);
+ if (fMD5String = TestMD5Hash) then
+ Result := True;
+end;
+
+function TCryptIniFile.VerifyString(const Section, Ident, TestValue: string): boolean;
+ // Matches both value and Hash
+var
+ sCorrect, sCorrectMD5: string;
+begin
+ Result := False;
+ if fPlainText = True then
+ Exit;
+ // Fetch the MD5 hash of the TestValue string
+ sCorrect := EncodeString(TestValue);
+ fMyDigest := MDString(sCorrect, MD_VERSION_5);
+ sCorrectMD5 := MDPrint(fMyDigest);
+
+ // Read the MD5 hash of the INI string (sets fMD5String) and get the value;
+ fMD5String := '';
+ sCorrect := ReadString(Section, Ident, '');
+ // DEBUG
+ {
+ ShowMessageFmt('sCorrectMD5=%s, fMD5String=%s, sCorrect=%s, TestValue=%s',
+ [sCorrectMD5,fMD5String,sCorrect,TestValue]);
+ }
+ if ((sCorrectMD5 = fMD5String) and (sCorrect = TestValue)) then
+ Result := True;
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.VerifyInteger(const Section, Ident: string;
+ TestValue: longint): boolean;
+ // Matches both value and Hash
+var
+ iCorrect: longint;
+ sCorrect, sCorrectMD5: string;
+begin
+ Result := False;
+ if fPlainText = True then
+ Exit;
+ iCorrect:=0;
+ if TestValue <> 0 then
+ iCorrect := EncodeInteger(TestValue); // Use fKey to scramble it
+ // Encode it as a string
+ sCorrect := EncodeString(IntToStr(iCorrect));
+ // Make an MD5 Hash of it (sets fMD5String)
+ fMyDigest := MDString(sCorrect, MD_VERSION_5);
+ sCorrectMD5 := MDPrint(fMyDigest);
+ // Use ReadInteger to get the fMD5String of the INI file entry
+ // And read the value
+ iCorrect := ReadInteger(Section, Ident, 9999); // Automatically uses fKey to unscramble
+ // Value and MD5Hash match?
+ if ((sCorrectMD5 = fMD5String) and (iCorrect = TestValue)) then
+ Result := True;
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.VerifyBool(const Section, Ident: string;
+ TestValue: boolean): boolean;
+begin
+ Result := False;
+ if fPlainText = True then
+ Exit;
+ Result := VerifyString(Section, Ident, BoolToChar(TestValue));
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.VerifyInt64(const Section, Ident: string;
+ TestValue: int64): boolean;
+begin
+ Result := False;
+ if fPlainText = True then
+ Exit;
+ Result := VerifyString(Section, Ident, IntToStr(TestValue));
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.VerifyFloat(const Section, Ident: string;
+ TestValue: double): boolean;
+begin
+ Result := False;
+ if fPlainText = True then
+ Exit;
+ Result := VerifyString(Section, Ident, FloatToStr(TestValue));
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.VerifyDate(const Section, Ident: string;
+ TestValue: TDateTime): boolean;
+begin
+ Result := False;
+ if fPlainText = True then
+ Exit;
+ Result := VerifyString(Section, Ident, DateToStr(TestValue));
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.VerifyTime(const Section, Ident: string;
+ TestValue: TDateTime): boolean;
+begin
+ Result := False;
+ if fPlainText = True then
+ Exit;
+ Result := VerifyString(Section, Ident, TimeToStr(TestValue));
+end;
+
+// ***************************************************************************//
+
+function TCryptIniFile.VerifyDateTime(const Section, Ident: string;
+ TestValue: TDateTime): boolean;
+begin
+ Result := False;
+ if fPlainText = True then
+ Exit;
+ Result := VerifyString(Section, Ident, DateTimeToStr(TestValue));
+end;
+
+// ***************************************************************************//
+
+{$IFDEF USE_DCPCRYPT}
+// TODO? use Stream property (Filename='')
+Function TCryptIniFile.EncryptINI(DeleteOld:Boolean = TRUE;sPassword: string = DEFAULT_KEYPHRASE;NewExt:String = DEFAULT_EXT):Boolean;
+var
+ Cipher: TDCP_rc4;
+ Source,Dest: TFileStream;
+begin
+ CacheUpdates:=FALSE;
+ UpdateFile;
+ Result:=FALSE;
+ If NOT FileExistsUTF8(Filename) then exit;
+ try
+ If sPassword = '' then sPassword:=fKeyPhrase;
+ Source := TFileStream.Create(Filename, fmOpenRead);
+ // INI.Filename, PATH SOURCE OF FILE
+ Dest := TFileStream.Create(ChangeFileExt(Filename, NewExt), fmCreate);
+ // ChangeFileExt(INI.Filename, NewExt), PATH DESTINATION OF FILE
+ Cipher := TDCP_rc4.Create(Nil);
+ Cipher.InitStr(sPassword, TDCP_sha1);
+ // initialize the cipher with a hash of the passphrase
+ Cipher.EncryptStream(Source, Dest, Source.Size);
+ // encrypt the contents of the file
+ Cipher.Burn;
+ Cipher.Free;
+ finally
+ Dest.Free;
+ Source.Free;
+ Result:=TRUE;
+ If DeleteOld then Result:=Result AND DeleteFile(Filename);
+ end;
+end;
+
+// ***************************************************************************//
+
+Function TCryptIniFile.DecryptINI(DeleteOld:Boolean = TRUE;sPassword: string = DEFAULT_KEYPHRASE;NewExt:String = DEFAULT_EXT):Boolean;
+var
+ Cipher: TDCP_rc4;
+ Source,Dest: TFileStream;
+begin
+ Result:=FALSE;
+ If NOT FileExistsUTF8(ChangeFileExt(Filename, NewExt)) then exit;
+ try
+ If sPassword = '' then sPassword:=fKeyPhrase;
+ Source := TFileStream.Create(ChangeFileExt(Filename, NewExt), fmOpenRead);
+ // ChangeFileExt(INI.Filename, NewExt), PATH SOURCE OF FILE
+ Dest := TFileStream.Create(Filename, fmCreate);
+ // INI.Filename, PATH DESTINATION OF FILE
+ Cipher := TDCP_rc4.Create(Nil);
+ Cipher.InitStr(sPassword, TDCP_sha1);
+ // initialize the cipher with a hash of the passphrase
+ Cipher.DecryptStream(Source, Dest, Source.Size);
+ // decrypt the contents of the file
+ Cipher.Burn;
+ Cipher.Free;
+ finally
+ Dest.Free;
+ Source.Free;
+ Result:=TRUE;
+ If DeleteOld then Result:=Result AND DeleteFile(ChangeFileExt(Filename, NewExt));
+ // Re-initialise Decrypted INI from disk
+ Try
+ {$IFDEF FPC311}
+ Create(Filename,Options);
+ {$else}
+ Create(Filename,EscapeLineFeeds);
+ {$endif}
+ CacheUpdates:=FALSE;
+ Except
+ // Eat exception
+ Result:=FALSE;
+ end;
+ end;
+end;
+{$ENDIF}
+
+// ***************************************************************************//
+// ENDOFFILE
+// ***************************************************************************//
+
+end.