
Component V 0.1.2.0 Demo V 0.1.2.0 git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5487 8e941d3f-bd1b-0410-a28a-d453659cc2b4
994 lines
36 KiB
ObjectPascal
994 lines
36 KiB
ObjectPascal
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.
|