diff --git a/README.txt b/README.txt index b490b5a..7b46089 100644 --- a/README.txt +++ b/README.txt @@ -14,7 +14,7 @@ CloneOfQuickHashProject/HashLib4Pascal/HashLib/src/Packages/FPC/HashLib4PascalPa then click the 'Compile' button. Then use the next button to the right called 'Use >>' and then click 'Add to Project' from the drop down menu. HashLib4Pascal is now added to your QuickHash project. -Repeat the same process for the package 'DateTimePicker' by Zoran Vucenovic (http://wiki.lazarus.freepascal.org/ZVDateTimeControls_Package) which was added in v2.8.3 to enable scheduled hashing. So simply choose 'Package' --> Open Package File (lpk)' from the top menu of Lazarus. Choose and navigate to: +Repeat the same process for the package 'DateTimePicker' by Zoran Vu?enovi? (http://wiki.lazarus.freepascal.org/ZVDateTimeControls_Package) which was added in v2.8.3 to enable scheduled hashing. So simply choose 'Package' --> Open Package File (lpk)' from the top menu of Lazarus. Choose and navigate to: CloneOfQuickHashProject/DateTimePicker/zvdatetimectrls.lpk, choose 'Compile' and then click 'Use >> Add to Project'. Better yet, choose “Install” so that it becomes a component of your Lazarus IDE, which will further reduce warnings. @@ -28,9 +28,11 @@ Ted Smith v2.8.3 (In Development) -Added scheduler to most tabs (File, FileS, Copy, Compare Two Files, Compare Directories, and Disk Hashing Module), meaning the user can set a date and time in the future to start the hashing process of their chosen data. For example, say the user is copying a large file to a certain location that won't finish copying for a few hours; perhaps after the user has left the office or gone to bed it will finish, but he wants to have the file hashed for the next day. With QuickHash v2.8.3, the user can set a date & time in the future (to hours and minutes precision, not seconds), to start a few hours ahead of time allowing the file to copy first, then select the file (given that even before it's finished copying, the file can still be selected) and QuickHash will start hashing at that time. For obvious reasons, there is no scheduler for basic text hashing! +A new tab (the first new tab since disk hashing was added a couple of years ago!!) dedicated to Base64 decoding. The user can now select a file or a folder of files that are all Base64 encoded, and QuickHash will compute the hashes of both the encoded and decoded versions of the file, without the user having to supply a Base64 decoded version. In addition, as a little extra, there’s also a third button in the new tab that the user can click to simply select a folder of Base64 encoded files and QuickHash will generate decoded versions of those files for the user with a slightly different filename. He can then go on to hash them if he so pleases, using the ‘File’ or ‘FileS’ tab of Quickhash. The user has to establish what file types he has though, and, for Windows users, this will mean adding the appropriate extension such as .pdf or .jpg to such files. Linux users will not have that problem because the operating system will work out what application is needed based on the file signature. -Added the package 'DateTimePicker' by Zoran Vucenovic (http://wiki.lazarus.freepascal.org/ZVDateTimeControls_Package). This is now also required to compile the project (see section 1 of README.txt) from source. +Added scheduler to most tabs (File, FileS, Copy and Compare), meaning the user can set a date and time in the future to start the hashing process. For example, say the user is copying a large file to a certain location that won't finish copying for a few hours; perhaps after the user has left the office or gone to bed it will finish, but he wants to have the file hashed for the next day. With QuickHash v2.8.3, the user can set a date & time in the future (to hours and minutes precision, not seconds), to start a few hours ahead of time allowing the file to copy first, then select the file (given that even before it's finished copying, the file can still be selected) and QuickHash will start hashing at that time. + +Added the package 'DateTimePicker' by Zoran Vu?enovi? (http://wiki.lazarus.freepascal.org/ZVDateTimeControls_Package). This is now also required to compile the project (see section 1 of README.txt) from source. In the “Compare Directories” tab, if the user chooses to tabulate the results, upon completion of the comparison, if the user single left mouse clicks in either grid, the corresponding row entry in the other grid will be selected, assuming both directories actually match. If they do not match, the user will be taken to the corresponding grid row count, but this is unlikely to contain the same filename data of course. diff --git a/diskmodule.lfm b/diskmodule.lfm index 9ed41b9..12c9305 100644 --- a/diskmodule.lfm +++ b/diskmodule.lfm @@ -1,7 +1,7 @@ object frmDiskHashingModule: TfrmDiskHashingModule - Left = 848 + Left = 1254 Height = 623 - Top = 267 + Top = 363 Width = 787 Caption = 'QuickHash v2.8.3 - Disk Hashing Module' ClientHeight = 623 diff --git a/unit2.lfm b/unit2.lfm index 3998bff..55bef26 100644 --- a/unit2.lfm +++ b/unit2.lfm @@ -1,7 +1,7 @@ object MainForm: TMainForm - Left = 363 + Left = 806 Height = 704 - Top = 114 + Top = 249 Width = 1018 AllowDropFiles = True Caption = 'QuickHash v2.8.3 - The easy and convenient way to hash data in Linux, OSX and Windows' @@ -16,14 +16,13 @@ object MainForm: TMainForm object PageControl1: TPageControl Left = 16 Height = 646 - Hint = 'Enter date and time (hours and minutes) ' Top = 24 Width = 986 - ActivePage = TabSheet1 + ActivePage = TabSheet8 Anchors = [akTop, akLeft, akRight, akBottom] ParentShowHint = False ShowHint = True - TabIndex = 0 + TabIndex = 7 TabOrder = 0 OnChange = PageControl1Change object TabSheet1: TTabSheet @@ -806,7 +805,7 @@ object MainForm: TMainForm object TabSheet4: TTabSheet Hint = 'Choose a directory, have its content hashed, files are copied to destination, and re-hashed.' Caption = '&Copy' - ClientHeight = 621 + ClientHeight = 618 ClientWidth = 978 ParentShowHint = False ShowHint = True @@ -836,7 +835,7 @@ object MainForm: TMainForm OnClick = Panel1CopyAndHashOptionsClick object CheckBoxListOfDirsOnly: TCheckBox Left = 14 - Height = 20 + Height = 21 Hint = 'Tick to have child directories listed, but no files inside hashed' Top = 0 Width = 161 @@ -846,29 +845,29 @@ object MainForm: TMainForm end object CheckBoxListOfDirsAndFilesOnly: TCheckBox Left = 14 - Height = 20 + Height = 21 Hint = 'Tick to have child directories and files listed, but no files actually hashed' Top = 32 - Width = 213 + Width = 214 Caption = 'Just LIST sub-directories and files' OnChange = CheckBoxListOfDirsAndFilesOnlyChange TabOrder = 1 end object SaveToHTMLCheckBox2: TCheckBox Left = 256 - Height = 20 + Height = 21 Hint = 'Save results as HTML web page' Top = 0 - Width = 142 + Width = 141 Caption = 'Save results (HTML)?' TabOrder = 2 end object SaveToCSVCheckBox2: TCheckBox Left = 256 - Height = 20 + Height = 21 Hint = 'Save results as CSV data (spreadsheet format)' Top = 32 - Width = 134 + Width = 130 Caption = 'Save results (CSV)?' Checked = True State = cbChecked @@ -876,17 +875,17 @@ object MainForm: TMainForm end object FileTypeMaskCheckBox1: TCheckBox Left = 448 - Height = 20 + Height = 21 Hint = 'Only copy files of a particular type' Top = 32 - Width = 123 + Width = 126 Caption = 'Choose file types?' OnChange = FileTypeMaskCheckBox1Change TabOrder = 5 end object FileMaskField: TEdit Left = 464 - Height = 24 + Height = 25 Hint = 'Use an asterix, full stop and the file type '#10'extension, seperated by a semi-colon.'#10'NO space characters' Top = 64 Width = 248 @@ -898,98 +897,98 @@ object MainForm: TMainForm end object chkNoRecursiveCopy: TCheckBox Left = 448 - Height = 20 + Height = 21 Hint = 'Only copy files found in the root of chosen directory' Top = 0 - Width = 152 + Width = 156 Caption = 'Ignore sub-directories?' TabOrder = 4 end object Label11: TLabel Left = 14 - Height = 16 + Height = 17 Top = 72 - Width = 78 + Width = 75 Caption = '# Files in Dir:' ParentColor = False end object Label12: TLabel Left = 14 - Height = 16 + Height = 17 Top = 104 - Width = 103 + Width = 100 Caption = '# Files Examined:' ParentColor = False end object Label13: TLabel Left = 14 - Height = 16 + Height = 17 Top = 136 - Width = 75 + Width = 74 Caption = '% Complete:' ParentColor = False end object lblTimeTaken5A: TLabel Left = 464 - Height = 16 + Height = 17 Top = 97 - Width = 70 + Width = 66 Caption = 'Start Time: ' ParentColor = False end object lblTimeTaken5B: TLabel Left = 464 - Height = 16 + Height = 17 Top = 121 - Width = 67 + Width = 65 Caption = 'End Time: ' ParentColor = False end object lblTimeTaken6A: TLabel Left = 560 - Height = 16 + Height = 17 Top = 97 - Width = 12 + Width = 9 Caption = '...' ParentColor = False end object lblTimeTaken6B: TLabel Left = 560 - Height = 16 + Height = 17 Top = 121 - Width = 12 + Width = 9 Caption = '...' ParentColor = False end object lblTimeTaken5C: TLabel Left = 464 - Height = 16 + Height = 17 Top = 145 - Width = 73 + Width = 69 Caption = 'Time Taken:' ParentColor = False end object lblTimeTaken6C: TLabel Left = 560 - Height = 16 + Height = 17 Top = 145 - Width = 12 + Width = 9 Caption = '...' ParentColor = False end object lblNoOfFilesToExamine: TLabel Left = 136 - Height = 16 + Height = 17 Top = 72 - Width = 12 + Width = 9 Caption = '...' ParentColor = False end object lblDataCopiedSoFar: TLabel Left = 208 - Height = 16 + Height = 17 Top = 104 - Width = 12 + Width = 9 Caption = '...' ParentColor = False end @@ -999,29 +998,29 @@ object MainForm: TMainForm AnchorSideTop.Control = pbCopy AnchorSideTop.Side = asrBottom AnchorSideRight.Control = pbCopy - Left = 375 - Height = 16 + Left = 377 + Height = 17 Top = 139 - Width = 12 + Width = 9 Anchors = [] Caption = '...' ParentColor = False end object chkNoPathReconstruction: TCheckBox Left = 624 - Height = 20 + Height = 21 Hint = 'Files will be copied to the root of your chosen '#10'destination directory. Duplicate file NAMES '#10'will have their names appended with '#10'DuplicatedNameX to avoid filesystem conflicts' Top = 0 - Width = 127 + Width = 132 Caption = 'Don''t rebuild path?' TabOrder = 7 end object chkCopyHidden: TCheckBox Left = 624 - Height = 20 + Height = 21 Hint = 'If checked, will find hidden directories and files and copy those too' Top = 32 - Width = 123 + Width = 127 Caption = 'Copy hidden files?' TabOrder = 8 end @@ -1035,20 +1034,20 @@ object MainForm: TMainForm end object lblschedulertickboxCopyTab: TCheckBox Left = 256 - Height = 20 + Height = 21 Hint = 'Tick and set a date and time ahead of current time'#13#10'and then select the source and destination folders.'#13#10'Hash, copy and hash will start at the specified time' Top = 64 - Width = 108 + Width = 105 Caption = 'Start at a time:' OnChange = lblschedulertickboxCopyTabChange TabOrder = 10 end object ZVDateTimePickerCopyTab: TZVDateTimePicker Left = 272 - Height = 24 + Height = 25 Hint = 'Enter date and time (hours and minutes) ' Top = 96 - Width = 138 + Width = 136 CenturyFrom = 1941 MaxDate = 73050 MinDate = 42736 @@ -1070,9 +1069,9 @@ object MainForm: TMainForm end object lblNoOfFilesToExamine2: TLabel Left = 136 - Height = 16 + Height = 17 Top = 104 - Width = 12 + Width = 9 Caption = '...' ParentColor = False end @@ -1095,7 +1094,7 @@ object MainForm: TMainForm end object Edit2SourcePath: TEdit Left = 8 - Height = 24 + Height = 25 Hint = 'Show currently selected source directory'#13#10'Switch to UNC path by ticking "UNC Mode?"' Top = 216 Width = 365 @@ -1107,7 +1106,7 @@ object MainForm: TMainForm end object Edit3DestinationPath: TEdit Left = 480 - Height = 24 + Height = 25 Hint = 'Show currently selected destination directory'#13#10'Switch to UNC path by ticking "UNC Mode?"' Top = 216 Width = 357 @@ -1119,8 +1118,8 @@ object MainForm: TMainForm end object StatusBar3: TStatusBar Left = 0 - Height = 20 - Top = 555 + Height = 23 + Top = 551 Width = 836 Panels = <> ParentShowHint = False @@ -1170,10 +1169,10 @@ object MainForm: TMainForm end object chkUNCMode: TCheckBox Left = 384 - Height = 20 + Height = 21 Hint = 'Tick to enter UNC network path '#13#10'instead of using treeview selection' Top = 216 - Width = 85 + Width = 92 Caption = 'UNC Mode?' OnChange = chkUNCModeChange TabOrder = 8 @@ -1212,7 +1211,7 @@ object MainForm: TMainForm end object TabSheet7: TTabSheet Caption = 'Compare Two Files' - ClientHeight = 621 + ClientHeight = 618 ClientWidth = 978 object AlgorithmChoiceRadioBox5: TRadioGroup Left = 16 @@ -1288,18 +1287,18 @@ object MainForm: TMainForm end object Label9: TLabel Left = 130 - Height = 16 + Height = 17 Top = 176 - Width = 40 + Width = 38 Caption = 'Result:' ParentColor = False end object lblFileBHash: TLabel Left = 130 - Height = 16 + Height = 17 Hint = 'Single left mouse click to copy value to clipboard' Top = 100 - Width = 12 + Width = 9 Caption = '...' ParentColor = False ParentShowHint = False @@ -1309,10 +1308,10 @@ object MainForm: TMainForm end object lblFileAHash: TLabel Left = 130 - Height = 16 + Height = 17 Hint = 'Single left mouse click to copy value to clipboard' Top = 40 - Width = 12 + Width = 9 Caption = '...' ParentColor = False ParentShowHint = False @@ -1322,7 +1321,7 @@ object MainForm: TMainForm end object lblHashMatchResult: TLabel Left = 180 - Height = 13 + Height = 15 Top = 176 Width = 9 Caption = '...' @@ -1344,14 +1343,14 @@ object MainForm: TMainForm AnchorSideLeft.Control = GroupBox4 AnchorSideRight.Control = GroupBox4 Left = 0 - Height = 20 - Top = 255 + Height = 23 + Top = 251 Width = 836 Panels = <> end object edtFileAName: TEdit Left = 128 - Height = 24 + Height = 25 Hint = 'Path to the file you wish to analyse. '#13#10'Type or paste path here directly, '#13#10'or use the button to the left to select it' Top = 9 Width = 648 @@ -1363,7 +1362,7 @@ object MainForm: TMainForm end object edtFileBName: TEdit Left = 128 - Height = 24 + Height = 25 Hint = 'Path to the second file you wish to analyse. '#13#10'Type or paste path here directly, '#13#10'or use the button to the left to select it' Top = 72 Width = 648 @@ -1375,20 +1374,20 @@ object MainForm: TMainForm end object lblschedulertickboxCompareTab: TCheckBox Left = 8 - Height = 20 + Height = 21 Hint = 'After choosing FileA and FileB, tick and set a date and time ahead of current time'#13#10'and then click Compare Now.' Top = 132 - Width = 108 + Width = 105 Caption = 'Start at a time:' OnChange = lblschedulertickboxCompareTabChange TabOrder = 7 end object ZVDateTimePickerCompareTab: TZVDateTimePicker Left = 128 - Height = 24 + Height = 25 Hint = 'Enter date and time (hours and minutes) ' Top = 132 - Width = 138 + Width = 136 CenturyFrom = 1941 MaxDate = 2958465 MinDate = 42736 @@ -1412,17 +1411,17 @@ object MainForm: TMainForm end object TabSheet6: TTabSheet Caption = 'Compare Directories' - ClientHeight = 621 + ClientHeight = 618 ClientWidth = 978 ParentShowHint = False object GroupBox1: TGroupBox Left = 120 - Height = 473 + Height = 470 Top = 10 Width = 842 Anchors = [akTop, akLeft, akRight, akBottom] Caption = 'Compare two directories' - ClientHeight = 451 + ClientHeight = 448 ClientWidth = 838 DragMode = dmAutomatic Font.Height = -13 @@ -1462,7 +1461,7 @@ object MainForm: TMainForm AnchorSideRight.Control = GroupBox1 AnchorSideRight.Side = asrBottom Left = 0 - Height = 124 + Height = 121 Hint = 'Single left click to find corresponding '#13#10'value in other grid' Top = 288 Width = 838 @@ -1487,9 +1486,9 @@ object MainForm: TMainForm end object lblStatusA: TLabel Left = 16 - Height = 13 + Height = 15 Top = 96 - Width = 43 + Width = 41 Caption = 'Status: ' Font.Style = [fsBold] ParentColor = False @@ -1497,18 +1496,18 @@ object MainForm: TMainForm end object lblDirAName: TLabel Left = 176 - Height = 16 + Height = 17 Top = 0 - Width = 12 + Width = 9 Caption = '...' ParentColor = False WordWrap = True end object lblDirBName: TLabel Left = 176 - Height = 16 + Height = 17 Top = 32 - Width = 12 + Width = 9 Caption = '...' ParentColor = False WordWrap = True @@ -1552,9 +1551,9 @@ object MainForm: TMainForm end object cbShowDetailsOfAllComparisons: TCheckBox Left = 176 - Height = 20 + Height = 21 Top = 64 - Width = 368 + Width = 372 Caption = 'Tabulate only encountered errors instead of all files (faster)?' Checked = True OnChange = cbShowDetailsOfAllComparisonsChange @@ -1563,10 +1562,10 @@ object MainForm: TMainForm end object ZVDateTimePickerCompareDirsTab: TZVDateTimePicker Left = 688 - Height = 24 + Height = 25 Hint = 'Enter date and time (hours and minutes) ' Top = 64 - Width = 138 + Width = 136 CenturyFrom = 1941 MaxDate = 2958465 MinDate = 42736 @@ -1588,10 +1587,10 @@ object MainForm: TMainForm end object lblschedulertickboxCompareDirsTab: TCheckBox Left = 568 - Height = 20 + Height = 21 Hint = 'After choosing FileA and FileB, tick and set a date and time ahead of current time'#13#10'and then click Compare Now.' Top = 64 - Width = 108 + Width = 105 Caption = 'Start at a time:' OnChange = lblschedulertickboxCompareTwoDirectoriesTabChange TabOrder = 7 @@ -1599,7 +1598,7 @@ object MainForm: TMainForm object StatusBar6: TStatusBar Left = 8 Height = 23 - Top = 121 + Top = 120 Width = 817 Align = alNone Anchors = [akLeft, akRight] @@ -1613,7 +1612,7 @@ object MainForm: TMainForm AnchorSideRight.Side = asrBottom Left = 120 Height = 161 - Top = 443 + Top = 440 Width = 842 Anchors = [akLeft, akRight, akBottom] Caption = 'Summary' @@ -1624,37 +1623,37 @@ object MainForm: TMainForm TabOrder = 1 object lblFileCountDiffA: TLabel Left = 8 - Height = 16 + Height = 17 Top = 8 - Width = 128 + Width = 127 Caption = 'File Count Difference: ' ParentColor = False end object lblFileCountDiffB: TLabel AnchorSideLeft.Control = lblFileCountDiffA AnchorSideLeft.Side = asrBottom - Left = 136 - Height = 16 + Left = 135 + Height = 17 Top = 8 - Width = 20 + Width = 17 Caption = ' ...' ParentColor = False end object lblHashMatchA: TLabel Left = 8 - Height = 16 + Height = 17 Top = 36 - Width = 84 + Width = 87 Caption = 'Hash Match? ' ParentColor = False end object lblHashMatchB: TLabel AnchorSideLeft.Control = lblHashMatchA AnchorSideLeft.Side = asrBottom - Left = 92 - Height = 16 + Left = 95 + Height = 17 Top = 36 - Width = 20 + Width = 17 Caption = ' ...' Constraints.MaxWidth = 190 ParentColor = False @@ -1662,58 +1661,58 @@ object MainForm: TMainForm end object lblTimeStartA: TLabel Left = 518 - Height = 16 + Height = 17 Top = 8 - Width = 80 + Width = 77 Caption = 'Time Started:' ParentColor = False end object lblTimeFinishedA: TLabel Left = 518 - Height = 16 + Height = 17 Top = 36 - Width = 85 + Width = 82 Caption = 'Time Finished:' ParentColor = False end object lblTimeTakenA: TLabel Left = 518 - Height = 16 + Height = 17 Top = 64 - Width = 73 + Width = 69 Caption = 'Time Taken:' ParentColor = False end object lblTimeStartB: TLabel Left = 617 - Height = 16 + Height = 17 Top = 8 - Width = 12 + Width = 9 Caption = '...' ParentColor = False end object lblTimeFinishedB: TLabel Left = 617 - Height = 16 + Height = 17 Top = 36 - Width = 12 + Width = 9 Caption = '...' ParentColor = False end object lblTimeTakenB: TLabel Left = 617 - Height = 16 + Height = 17 Top = 64 - Width = 12 + Width = 9 Caption = '...' ParentColor = False end object lblTotalFileCountA: TLabel AnchorSideRight.Control = lblTotalFileCountNumberA Left = 517 - Height = 16 + Height = 17 Top = 95 - Width = 102 + Width = 99 BorderSpacing.Left = 5 Caption = '# Files in Dir A : ' ParentColor = False @@ -1723,19 +1722,19 @@ object MainForm: TMainForm AnchorSideLeft.Side = asrBottom AnchorSideRight.Control = GroupBox2 AnchorSideRight.Side = asrBottom - Left = 619 - Height = 16 + Left = 616 + Height = 17 Top = 95 - Width = 20 + Width = 17 Caption = ' ...' ParentColor = False end object lblTotalFileCountB: TLabel AnchorSideRight.Control = lblTotalFileCountNumberB Left = 517 - Height = 16 + Height = 17 Top = 120 - Width = 101 + Width = 98 BorderSpacing.Left = 5 Caption = '# Files in Dir B : ' ParentColor = False @@ -1745,10 +1744,10 @@ object MainForm: TMainForm AnchorSideLeft.Side = asrBottom AnchorSideRight.Control = GroupBox2 AnchorSideRight.Side = asrBottom - Left = 618 - Height = 16 + Left = 615 + Height = 17 Top = 120 - Width = 20 + Width = 17 Caption = ' ...' ParentColor = False end @@ -1862,6 +1861,176 @@ object MainForm: TMainForm ParentColor = False end end + object TabSheet8: TTabSheet + Caption = 'Base64 Data' + ClientHeight = 618 + ClientWidth = 978 + object AlgorithmChoiceRadioBox7: TRadioGroup + Left = 16 + Height = 129 + Top = 10 + Width = 104 + AutoFill = True + Caption = 'Algorithm' + ChildSizing.LeftRightSpacing = 6 + ChildSizing.TopBottomSpacing = 6 + ChildSizing.EnlargeHorizontal = crsHomogenousChildResize + ChildSizing.EnlargeVertical = crsHomogenousChildResize + ChildSizing.ShrinkHorizontal = crsScaleChilds + ChildSizing.ShrinkVertical = crsScaleChilds + ChildSizing.Layout = cclLeftToRightThenTopToBottom + ChildSizing.ControlsPerLine = 1 + ClientHeight = 107 + ClientWidth = 100 + Font.Height = -13 + ItemIndex = 1 + Items.Strings = ( + 'MD5' + 'SHA-1' + 'SHA256' + 'SHA512' + 'xxHash' + ) + OnClick = AlgorithmChoiceRadioBox3Click + ParentFont = False + TabOrder = 0 + end + object TextHashingGroupBox1: TGroupBox + Left = 120 + Height = 574 + Top = 10 + Width = 844 + Anchors = [akTop, akLeft, akRight] + Caption = 'Base64 Decoder and Hasher' + ClientHeight = 552 + ClientWidth = 840 + Color = clForm + Font.Height = -13 + ParentColor = False + ParentFont = False + TabOrder = 1 + object btnB64FileChooser: TButton + Left = 8 + Height = 25 + Top = 16 + Width = 131 + Caption = 'Select a File...' + OnClick = btnB64FileChooserClick + TabOrder = 0 + end + object btnB64FileSChooser: TButton + Left = 8 + Height = 25 + Top = 168 + Width = 131 + Caption = 'Select a Folder...' + OnClick = btnB64FileSChooserClick + TabOrder = 1 + end + object btnB64JustDecodeFiles: TButton + Left = 8 + Height = 25 + Hint = 'Just a useful feature to allow you to decode Base64 files. '#13#10'Original files are left in situ. New decoded copies are made'#13#10'and saved to the same folder. No hashing conducted.' + Top = 464 + Width = 163 + Caption = 'Decode Base64 Files...' + OnClick = btnB64JustDecodeFilesClick + TabOrder = 2 + end + object b64StringGrid1File: TStringGrid + Left = 8 + Height = 96 + Top = 56 + Width = 816 + Anchors = [akTop, akLeft, akRight] + ColCount = 4 + Columns = < + item + Title.Caption = 'Filename' + Width = 250 + end + item + Title.Caption = 'Encoded Hash' + Width = 250 + end + item + Title.Caption = 'Decoded Hash' + Width = 250 + end> + Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goColSizing, goRowSelect, goDblClickAutoSize, goSmoothScroll] + PopupMenu = b64FileGridPopupMenu + RowCount = 2 + TabOrder = 3 + TitleFont.Height = -13 + end + object b64StringGrid2FileS: TStringGrid + Left = 8 + Height = 224 + Top = 216 + Width = 816 + Anchors = [akTop, akLeft, akRight] + AutoAdvance = aaDown + ColCount = 4 + ColumnClickSorts = True + Columns = < + item + Title.Caption = 'Filename' + Width = 250 + end + item + Title.Caption = 'Encoded Hash' + Width = 250 + end + item + Title.Caption = 'Decoded Hash' + Width = 250 + end> + Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goColSizing, goRowSelect, goDblClickAutoSize, goSmoothScroll] + PopupMenu = b64FilesGridPopupMenu + RangeSelectMode = rsmMulti + RowCount = 2 + TabOrder = 4 + TitleFont.Height = -13 + end + object b64ProgressFileS: TEdit + Left = 192 + Height = 25 + Hint = 'Progress of Base64 decoding of FileS will show here' + Top = 168 + Width = 628 + Anchors = [akTop, akLeft, akRight] + Color = clSilver + TabOrder = 5 + end + object b64DecoderProgress: TEdit + Left = 192 + Height = 25 + Hint = 'Progress of Base64 decoding of FileS will show here' + Top = 464 + Width = 628 + Anchors = [akTop, akLeft, akRight] + Color = clSilver + TabOrder = 6 + end + object lblB64DecoderWarning: TLabel + Left = 8 + Height = 17 + Top = 504 + Width = 9 + Caption = '...' + ParentColor = False + WordWrap = True + end + object lblB64Warning: TLabel + Left = 200 + Height = 17 + Top = 16 + Width = 496 + Caption = 'No checks are conducted to ensure your source data is Base64 encoded to start with' + ParentColor = False + end + end + end end object Label15: TLabel Left = 200 @@ -2046,4 +2215,44 @@ object MainForm: TMainForm left = 56 top = 248 end + object b64FileChooserDialog: TOpenDialog + left = 904 + top = 200 + end + object b64FileSChooserDialog: TSelectDirectoryDialog + left = 904 + top = 248 + end + object b64FilesGridPopupMenu: TPopupMenu + left = 904 + top = 360 + object MenuItem3: TMenuItem + Caption = 'Copy to Grid to Clipboard' + OnClick = MenuItem3Click + end + object MenuItem4: TMenuItem + Caption = 'Copy Row to Clipboard' + OnClick = MenuItem4Click + end + object MenuItem5: TMenuItem + Caption = 'Save grid to file...' + OnClick = MenuItem5Click + end + end + object b64FileGridPopupMenu: TPopupMenu + left = 904 + top = 160 + object MenuItem6: TMenuItem + Caption = 'Copy Row to Clipboard' + OnClick = MenuItem6Click + end + end + object b64FileSDecoderDialog: TSelectDirectoryDialog + left = 904 + top = 296 + end + object b64SaveDialog: TSaveDialog + left = 904 + top = 424 + end end diff --git a/unit2.pas b/unit2.pas index 54e7eb5..c1e7e71 100644 --- a/unit2.pas +++ b/unit2.pas @@ -74,7 +74,7 @@ interface Classes, SysUtils, Strutils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, Menus, ComCtrls, LazUTF8, LazUTF8Classes, LazFileUtils, Grids, ExtCtrls, sysconst, lclintf, ShellCtrls, XMLPropStorage, - uDisplayGrid, diskmodule, clipbrd, ZVDateTimePicker, frmAboutUnit, + uDisplayGrid, diskmodule, clipbrd, ZVDateTimePicker, frmAboutUnit, base64, FindAllFilesEnhanced, // an enhanced version of FindAllFiles, to ensure hidden files are found, if needed @@ -120,6 +120,10 @@ TMainForm = class(TForm) AlgorithmChoiceRadioBox1: TRadioGroup; AlgorithmChoiceRadioBox6: TRadioGroup; AlgorithmChoiceRadioBox5: TRadioGroup; + AlgorithmChoiceRadioBox7: TRadioGroup; + b64FileGridPopupMenu: TPopupMenu; + b64DecoderProgress: TEdit; + b64StringGrid2FileS: TStringGrid; btnClearTextArea: TButton; btnCompare: TButton; btnCompareTwoFiles: TButton; @@ -140,9 +144,15 @@ TMainForm = class(TForm) btnStopScan1: TButton; btnStopScan2: TButton; btnClearHashField: TButton; + btnB64FileChooser: TButton; + btnB64FileSChooser: TButton; + btnB64JustDecodeFiles: TButton; Button8CopyAndHash: TButton; cbToggleInputDataToOutputFile: TCheckBox; cbShowDetailsOfAllComparisons: TCheckBox; + b64ProgressFileS: TEdit; + lblB64Warning: TLabel; + lblB64DecoderWarning: TLabel; lblNoOfFilesToExamine2: TLabel; lblschedulertickboxCompareTab: TCheckBox; lblschedulertickboxCompareDirsTab: TCheckBox; @@ -184,13 +194,23 @@ TMainForm = class(TForm) MenuItem1C: TMenuItem; MenuItem1A: TMenuItem; MenuItem1B: TMenuItem; + b64FileChooserDialog: TOpenDialog; + MenuItem3: TMenuItem; + MenuItem4: TMenuItem; + MenuItem5: TMenuItem; + MenuItem6: TMenuItem; pbFileS: TProgressBar; pbCopy: TProgressBar; pbCompareDirA: TProgressBar; pbCompareDirB: TProgressBar; + b64FilesGridPopupMenu: TPopupMenu; + b64SaveDialog: TSaveDialog; SaveErrorsCompareDirsSaveDialog8: TSaveDialog; + b64FileSChooserDialog: TSelectDirectoryDialog; + b64FileSDecoderDialog: TSelectDirectoryDialog; StatusBar5: TStatusBar; StatusBar6: TStatusBar; + b64StringGrid1File: TStringGrid; SystemRAMGroupBox: TGroupBox; ImageList1: TImageList; lblRAM: TLabel; @@ -288,9 +308,11 @@ TMainForm = class(TForm) TabSheet5: TTabSheet; TabSheet6: TTabSheet; TabSheet7: TTabSheet; + TabSheet8: TTabSheet; TextHashingGroupBox: TGroupBox; QH_MainFormXMLPropStorage: TXMLPropStorage; SchedulerTimer: TTimer; + TextHashingGroupBox1: TGroupBox; ZVDateTimePickerCompareDirsTab: TZVDateTimePicker; ZVDateTimePickerCopyTab : TZVDateTimePicker; ZVDateTimePickerCompareTab : TZVDateTimePicker; @@ -302,9 +324,12 @@ TMainForm = class(TForm) procedure AlgorithmChoiceRadioBox4Click(Sender: TObject); procedure AlgorithmChoiceRadioBox5Click(Sender: TObject); procedure AlgorithmChoiceRadioBox6Click(Sender: TObject); + procedure btnB64FileSChooserClick(Sender: TObject); procedure btnClearHashFieldClick(Sender: TObject); procedure btnClearHashFieldKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure btnB64FileChooserClick(Sender: TObject); + procedure btnB64JustDecodeFilesClick(Sender: TObject); procedure cbShowDetailsOfAllComparisonsChange(Sender: TObject); procedure cbToggleInputDataToOutputFileChange(Sender: TObject); procedure lblDonateClick(Sender: TObject); @@ -322,6 +347,10 @@ TMainForm = class(TForm) procedure MenuItem1Click(Sender: TObject); procedure MenuItem2AClick(Sender: TObject); procedure MenuItem1CClick(Sender: TObject); + procedure MenuItem3Click(Sender: TObject); + procedure MenuItem4Click(Sender: TObject); + procedure MenuItem5Click(Sender: TObject); + procedure MenuItem6Click(Sender: TObject); procedure PageControl1Change(Sender: TObject); procedure Panel1CopyAndHashOptionsClick(Sender: TObject); procedure sgDirAClick(Sender: TObject); @@ -1294,6 +1323,39 @@ procedure TMainForm.MenuItem1CClick(Sender: TObject); MainForm.Close; end; +procedure TMainForm.MenuItem3Click(Sender: TObject); +begin + b64StringGrid2FileS.CopyToClipboard(false); + Showmessage('All grid rows copied to clipboard OK'); +end; + +procedure TMainForm.MenuItem4Click(Sender: TObject); +begin + b64StringGrid2FileS.CopyToClipboard(true); + Showmessage('Grid row data copied to clipboard OK'); +end; + +// Save data from the Base64 hashing data grid to TSV text file +procedure TMainForm.MenuItem5Click(Sender: TObject); +begin + b64SaveDialog.Title := 'Save grid results as...'; + b64SaveDialog.InitialDir := GetCurrentDir; + b64SaveDialog.Filter := 'Comma Sep|*.csv'; + b64SaveDialog.DefaultExt := 'csv'; + + if b64SaveDialog.Execute then + begin + b64StringGrid2FileS.SaveToCSVFile(b64SaveDialog.FileName); + ShowMessage('Grid data saved as ' + b64SaveDialog.FileName); + end; +end; + +procedure TMainForm.MenuItem6Click(Sender: TObject); +begin + b64StringGrid1File.CopyToClipboard(true); + Showmessage('Grid row data copied to clipboard OK'); +end; + procedure TMainForm.cbShowDetailsOfAllComparisonsChange(Sender: TObject); begin if cbShowDetailsOfAllComparisons.Checked then @@ -1366,6 +1428,90 @@ procedure TMainForm.AlgorithmChoiceRadioBox6Click(Sender: TObject); AlgorithmChoiceRadioBox5.ItemIndex := AlgorithmChoiceRadioBox6.ItemIndex; end; +// Recursively find, decode and then hash Base64 encoded files +procedure TMainForm.btnB64FileSChooserClick(Sender: TObject); +var + TotalB64FilesToExamine : TStringList; + i : integer; + DecodedStream : TMemoryStream; + EncodedStream : TFileStream; + Decoder : TBase64DecodingStream; + DirToHash, HashValA, HashValB : string; +begin + i := 0; + HashValA := ''; + HashValB := ''; + + if b64FileSChooserDialog.Execute then + begin + {$ifdef Windows} + LongPathOverride := '\\?\'; + {$endif} + + // Where we are going to look for Base64 files, then create a list of those files + DirToHash := b64FileSChooserDialog.FileName; + try + TotalB64FilesToExamine := TStringList.Create; + b64ProgressFileS.Caption := 'Finding and counting files...please wait'; + Application.ProcessMessages; + // Ensure files in long paths on that Windows OS can be examined...urrggh + {$ifdef Windows} + TotalB64FilesToExamine := FindAllFilesEx(LongPathOverride+DirToHash, '*', False, True); + {$else} + TotalB64FilesToExamine := FindAllFilesEx(DirToHash, '*', False, True); + {$endif} + finally + end; + + // Make sure the display grid has sufficient rows for the file count + b64StringGrid2FileS.RowCount := TotalB64FilesToExamine.Count + 1; + + // For each file, compute the Base64 encoded and decoded values and output to grid + for i := 0 to TotalB64FilesToExamine.Count -1 do + begin + Application.ProcessMessages; + b64ProgressFileS.Text:= 'Currently decoding and hashing ' + ExtractFileName(TotalB64FilesToExamine.Strings[i]); + HashValA := CalcTheHashFile(TotalB64FilesToExamine.Strings[i]); + try + EncodedStream := TFileStream.Create(TotalB64FilesToExamine.Strings[i], fmOpenRead); + try + DecodedStream := TMemoryStream.Create; + Decoder := TBase64DecodingStream.Create(EncodedStream); + DecodedStream.CopyFrom(Decoder, Decoder.Size); + // Create a temporary copy of the decoded file to hash it + DecodedStream.SaveToFile((TotalB64FilesToExamine.Strings[i]) + '-Base64Decoded'); + try + HashValB := CalcTheHashFile((TotalB64FilesToExamine.Strings[i]) + '-Base64Decoded'); + // And now delete the temporary copy + SysUtils.DeleteFile(TotalB64FilesToExamine.Strings[i] + '-Base64Decoded'); + finally + end; + finally + DecodedStream.Free; + Decoder.Free; + end; + finally + EncodedStream.Free; + end; + b64StringGrid2FileS.Cells[0, i+1] := IntToStr(i); + {$ifdef Windows} + b64StringGrid2FileS.Cells[1, i+1] := RemoveLongPathOverrideChars(TotalB64FilesToExamine.Strings[i], LongPathOverride); + {$else} + b64StringGrid2FileS.Cells[1, i+1] := TotalB64FilesToExamine.Strings[i]; + {$endif} + b64StringGrid2FileS.Cells[2, i+1] := HashValA; + b64StringGrid2FileS.Cells[3, i+1] := HashValB; + end; + end; + if assigned(TotalB64FilesToExamine) then TotalB64FilesToExamine.Free; + + // Reset the long path override for any other procedures triggered + LongPathOverride := ''; + b64ProgressFileS.Caption:= 'Decoded and hashed ' + IntToStr(i) + ' files. Completed at ' + FormatDateTime('YYYY/MM/DD HH:MM:SS', Now); +end; + + + // New to v2.8.3, to better facilitate use of the Expected Hash field procedure TMainForm.btnClearHashFieldClick(Sender: TObject); begin @@ -1379,6 +1525,113 @@ procedure TMainForm.btnClearHashFieldKeyDown(Sender: TObject; var Key: Word; lbleExpectedHash.Text:= ''; end; +// Select, decode and then hash a Base64 encoded file +procedure TMainForm.btnB64FileChooserClick(Sender: TObject); +var + DecodedStream : TMemoryStream; + EncodedStream : TFileStream; + Decoder : TBase64DecodingStream; + HashValA, HashValB : string; +begin + if b64FileChooserDialog.Execute then + begin + b64StringGrid1File.Clear; + // Compute hash of encoded file first + HashValA := CalcTheHashFile(b64FileChooserDialog.FileName); + // Now compute hash of decoded file + try + EncodedStream := TFileStream.Create(b64FileChooserDialog.FileName, fmOpenRead); + try + DecodedStream := TMemoryStream.Create; + Decoder := TBase64DecodingStream.Create(EncodedStream); + DecodedStream.CopyFrom(Decoder, Decoder.Size); + DecodedStream.SaveToFile((b64FileChooserDialog.FileName) + '-Base64Decoded'); + try + HashValB := CalcTheHashFile((b64FileChooserDialog.FileName) + '-Base64Decoded'); + SysUtils.DeleteFile(b64FileChooserDialog.FileName + '-Base64Decoded'); + finally + b64StringGrid1File.RowCount:= 2; + b64StringGrid1File.Cells[0,1] := '1'; + b64StringGrid1File.Cells[1,1] := b64FileChooserDialog.FileName; + b64StringGrid1File.Cells[2,1] := HashValA; + b64StringGrid1File.Cells[3,1] := HashValB; + end; + finally + DecodedStream.Free; + Decoder.Free; + end; + finally + EncodedStream.Free; + end; + end; +end; + +// Recursively find and decode all Base64 encoded files found in the selected folder +// Output decoded versions with the appended name '-Base64Decoded' +// So taking the example of a PDF that may be Base64 encoded as an e-mail attachment: +// MsgAttachment.emlx, which is the encoded attachment, becomes MsgAttachment.emlx-Base64Decoded +// and the user will then have to adjust the extension to MsgAttachment.emlx-Base64Decoded.pdf +procedure TMainForm.btnB64JustDecodeFilesClick(Sender: TObject); +var + TotalB64FilesToExamine : TStringList; + i : integer; + DecodedStream : TMemoryStream; + EncodedStream : TFileStream; + Decoder : TBase64DecodingStream; + DirToDecode : string; +begin + lblB64DecoderWarning.Caption := ''; + i := 0; + if b64FileSDecoderDialog.Execute then + begin + {$ifdef Windows} + LongPathOverride := '\\?\'; + {$endif} + + // Where we are going to look for Base64 files, then create a list of those files + DirToDecode := b64FileSDecoderDialog.FileName; + try + TotalB64FilesToExamine := TStringList.Create; + b64DecoderProgress.Caption := 'Finding and counting files...please wait'; + Application.ProcessMessages; + // Ensure files in long paths on that Windows OS can be examined...urrggh + {$ifdef Windows} + TotalB64FilesToExamine := FindAllFilesEx(LongPathOverride+DirToDecode, '*', False, True); + {$else} + TotalB64FilesToExamine := FindAllFilesEx(DirToDecode, '*', False, True); + {$endif} + finally + end; + + // For each file, compute the Base64 encoded and decoded values and output to grid + for i := 0 to TotalB64FilesToExamine.Count -1 do + begin + Application.ProcessMessages; + b64DecoderProgress.Text:= 'Currently decoding ' + ExtractFileName(TotalB64FilesToExamine.Strings[i] + ' ...please wait'); + try + EncodedStream := TFileStream.Create(TotalB64FilesToExamine.Strings[i], fmOpenRead); + try + DecodedStream := TMemoryStream.Create; + Decoder := TBase64DecodingStream.Create(EncodedStream); + DecodedStream.CopyFrom(Decoder, Decoder.Size); + // Create a temporary copy of the decoded file to hash it + DecodedStream.SaveToFile((TotalB64FilesToExamine.Strings[i]) + '-Base64Decoded'); + finally + DecodedStream.Free; + Decoder.Free; + end; + finally + EncodedStream.Free; + end; + end; + TotalB64FilesToExamine.Free; + end; + // Reset the long path override for any other procedures triggered + LongPathOverride := ''; + b64DecoderProgress.Caption:= 'Decoded ' + IntToStr(i) + ' Base64 files. Completed at ' + FormatDateTime('YYYY/MM/DD HH:MM:SS', Now); + lblB64DecoderWarning.Caption := 'Add appropriate file extensions to your decoded files. e.g MsgAttach-Base64Decoded to MsgAttach-Base64Decoded.pdf'; +end; + procedure TMainForm.PageControl1Change(Sender: TObject); begin @@ -2923,6 +3176,7 @@ function TMainForm.CalcTheHashFile(FileToBeHashed:string):string; 3: TabRadioGroup2 := AlgorithmChoiceRadioBox4; //RadioGroup for Copy. 4: TabRadioGroup2 := AlgorithmChoiceRadioBox5; //RadioGroup for Compare Two Files. 5: TabRadioGroup2 := AlgorithmChoiceRadioBox6; //RadioGroup for Compare Direcories. + 7: TabRadioGroup2 := AlgorithmChoiceRadioBox7; //RadioGroup for Base64 end; { For each hash instance, it has to be created, then initialised, populated,