From 3c67d36bff935052cad65ce3326cb260daa2aab4 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Sun, 8 Jun 2025 12:20:00 +0200 Subject: [PATCH 01/19] =?UTF-8?q?Add=20[Files]=20flag=20"download"=20for?= =?UTF-8?q?=20integrated=20download=20support.=20Was=20rather=20easy=20?= =?UTF-8?q?=F0=9F=91=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Todo: -Handle username + password -Add param for .issig url, like IssigSource? Now it requires a second entry -Doc -Allow download+extractarchive? I suppose the download part would need to be integrated differently since it first would need to download (all?) the archive(s) to {tmp} using CreateDownloadPage. Hooking up the download steam to the 7-Zip instream wouldn't work since it needs non-sequential access. --- Files/Default.isl | 2 + Projects/Src/Compiler.Messages.pas | 3 +- Projects/Src/Compiler.SetupCompiler.pas | 41 ++++- .../Src/Compression.SevenZipDLLDecoder.pas | 2 +- Projects/Src/IDE.ScintStylerInnoSetup.pas | 9 +- Projects/Src/Setup.Install.pas | 151 ++++++++++++++++-- Projects/Src/Setup.MainFunc.pas | 40 +++-- Projects/Src/Shared.SetupMessageIDs.pas | 2 + Projects/Src/Shared.Struct.pas | 7 +- 9 files changed, 215 insertions(+), 42 deletions(-) diff --git a/Files/Default.isl b/Files/Default.isl index 607e0225..06b967d6 100644 --- a/Files/Default.isl +++ b/Files/Default.isl @@ -285,6 +285,7 @@ AbortRetryIgnoreCancel=Cancel installation StatusClosingApplications=Closing applications... StatusCreateDirs=Creating directories... StatusExtractFiles=Extracting files... +StatusDownloadFiles=Downloading files... StatusCreateIcons=Creating shortcuts... StatusCreateIniEntries=Creating INI entries... StatusCreateRegistryEntries=Creating registry entries... @@ -338,6 +339,7 @@ ErrorChangingAttr=An error occurred while trying to change the attributes of the ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory: ErrorReadingSource=An error occurred while trying to read the source file: ErrorCopying=An error occurred while trying to copy a file: +ErrorDownloading=An error occurred while trying to download a file: ErrorExtracting=An error occurred while trying to extract an archive: ErrorReplacingExistingFile=An error occurred while trying to replace the existing file: ErrorRestartReplace=RestartReplace failed: diff --git a/Projects/Src/Compiler.Messages.pas b/Projects/Src/Compiler.Messages.pas index 1230ad9f..044734f4 100644 --- a/Projects/Src/Compiler.Messages.pas +++ b/Projects/Src/Compiler.Messages.pas @@ -223,6 +223,7 @@ const SCompilerParamFlagMissing = 'Flag "%s" must be used if flag "%s" is used'; SCompilerParamFlagMissing2 = 'Flag "%s" must be used if parameter "%s" is used'; SCompilerParamFlagMissing3 = 'Flag "%s" must be used if flags "%s" and "%s" are both used'; + SCompilerParamFlagMissingParam = 'Parameter "%s" must be specified if flag "%s" is used'; { Types, components, tasks, check, beforeinstall, afterinstall } SCompilerParamUnknownType = 'Parameter "%s" includes an unknown type'; @@ -290,8 +291,6 @@ const SCompilerFilesWildcardNotMatched = 'No files found matching "%s"'; SCompilerFilesDestNameCantBeSpecified = 'Parameter "DestName" cannot be specified if ' + 'the "Source" parameter contains wildcards or flag "extractarchive" is used'; - SCompilerFilesStrongAssemblyNameMustBeSpecified = 'Parameter "StrongAssemblyName" must be specified if ' + - 'the flag "gacinstall" is used'; SCompilerFilesCantHaveNonExternalExternalSize = 'Parameter "ExternalSize" may only be used when ' + 'the "external" flag is used'; SCompilerFilesExcludeTooLong = 'Parameter "Excludes" contains a pattern that is too long'; diff --git a/Projects/Src/Compiler.SetupCompiler.pas b/Projects/Src/Compiler.SetupCompiler.pas index 3bb42e82..536d0d14 100644 --- a/Projects/Src/Compiler.SetupCompiler.pas +++ b/Projects/Src/Compiler.SetupCompiler.pas @@ -4669,7 +4669,8 @@ type TParam = (paFlags, paSource, paDestDir, paDestName, paCopyMode, paAttribs, paPermissions, paFontInstall, paExcludes, paExternalSize, paExtractArchivePassword, paStrongAssemblyName, paISSigAllowedKeys, paComponents, paTasks, paLanguages, - paCheck, paBeforeInstall, paAfterInstall, paMinVersion, paOnlyBelowVersion); + paCheck, paBeforeInstall, paAfterInstall, paMinVersion, paOnlyBelowVersion, + paDownloadUserName, paDownloadPassword); const ParamFilesSource = 'Source'; ParamFilesDestDir = 'DestDir'; @@ -4683,6 +4684,8 @@ const ParamFilesExtractArchivePassword = 'ExtractArchivePassword'; ParamFilesStrongAssemblyName = 'StrongAssemblyName'; ParamFilesISSigAllowedKeys = 'ISSigAllowedKeys'; + ParamFilesDownloadUserName = 'DownloadUserName'; + ParamFilesDownloadPassword = 'DownloadPassword'; ParamInfo: array[TParam] of TParamInfo = ( (Name: ParamCommonFlags; Flags: []), (Name: ParamFilesSource; Flags: [piRequired, piNoEmpty, piNoQuotes]), @@ -4697,6 +4700,8 @@ const (Name: ParamFilesExtractArchivePassword; Flags: []), (Name: ParamFilesStrongAssemblyName; Flags: [piNoEmpty]), (Name: ParamFilesISSigAllowedKeys; Flags: [piNoEmpty]), + (Name: ParamFilesDownloadUserName; Flags: [piNoEmpty]), + (Name: ParamFilesDownloadPassword; Flags: [piNoEmpty]), (Name: ParamCommonComponents; Flags: []), (Name: ParamCommonTasks; Flags: []), (Name: ParamCommonLanguages; Flags: []), @@ -4705,7 +4710,7 @@ const (Name: ParamCommonAfterInstall; Flags: []), (Name: ParamCommonMinVersion; Flags: []), (Name: ParamCommonOnlyBelowVersion; Flags: [])); - Flags: array[0..42] of PChar = ( + Flags: array[0..43] of PChar = ( 'confirmoverwrite', 'uninsneveruninstall', 'isreadme', 'regserver', 'sharedfile', 'restartreplace', 'deleteafterinstall', 'comparetimestamp', 'fontisnttruetype', 'regtypelib', 'external', @@ -4717,7 +4722,7 @@ const 'uninsnosharedfileprompt', 'createallsubdirs', '32bit', '64bit', 'solidbreak', 'setntfscompression', 'unsetntfscompression', 'sortfilesbyname', 'gacinstall', 'sign', 'signonce', 'signcheck', - 'issigverify', 'extractarchive'); + 'issigverify', 'download', 'extractarchive'); SignFlags: array[TFileLocationSign] of String = ( '', 'sign', 'signonce', 'signcheck'); AttribsFlags: array[0..3] of PChar = ( @@ -5258,7 +5263,8 @@ begin 39: ApplyNewSign(Sign, fsOnce, SCompilerParamErrorBadCombo2); 40: ApplyNewSign(Sign, fsCheck, SCompilerParamErrorBadCombo2); 41: Include(Options, foISSigVerify); - 42: Include(Options, foExtractArchive); + 42: Include(Options, foDownload); + 43: Include(Options, foExtractArchive); end; { Source } @@ -5344,6 +5350,12 @@ begin Include(Options, foExternalSizePreset); end; + { DownloadUserName } + DownloadUserName := Values[paDownloadUserName].Data; + + { DownloadPassword } + DownloadPassword := Values[paDownloadPassword].Data; + { ExtractArchivePassword } ExtractArchivePassword := Values[paExtractArchivePassword].Data; @@ -5417,7 +5429,7 @@ begin end; if (foGacInstall in Options) and (AStrongAssemblyName = '') then - AbortCompile(SCompilerFilesStrongAssemblyNameMustBeSpecified); + AbortCompileFmt(SCompilerParamFlagMissingParam, ['StrongAssemblyName', 'gacinstall']); if AStrongAssemblyName <> '' then StrongAssemblyName := AStrongAssemblyName; @@ -5431,6 +5443,23 @@ begin Excludes := AExcludes.DelimitedText; end; + if foDownload in Options then begin + if not ExternalFile then + AbortCompileFmt(SCompilerParamFlagMissing, ['external', 'download']) + else if not(foIgnoreVersion in Options) then + AbortCompileFmt(SCompilerParamFlagMissing, ['ignoreversion', 'download']) + else if foExtractArchive in Options then + AbortCompileFmt(SCompilerParamErrorBadCombo2, [ParamCommonFlags, 'download', 'extractarchive']) + else if foCompareTimeStamp in Options then + AbortCompileFmt(SCompilerParamErrorBadCombo2, [ParamCommonFlags, 'download', 'comparetimestamp']) + else if RecurseSubdirs then + AbortCompileFmt(SCompilerParamErrorBadCombo2, [ParamCommonFlags, 'recursesubdirs', 'download']) + else if ADestName = '' then + AbortCompileFmt(SCompilerParamFlagMissingParam, ['DestName', 'download']) + else if not(foExternalSizePreset in Options) then + AbortCompileFmt(SCompilerParamFlagMissingParam, ['ExternalSize', 'download']); + end; + if foExtractArchive in Options then begin if not ExternalFile then AbortCompileFmt(SCompilerParamFlagMissing, ['external', 'extractarchive']) @@ -5468,7 +5497,7 @@ begin (Copy(ADestDir, 1, Length('{syswow64}')) = '{syswow64}') then WarningsList.Add(SCompilerFilesWarningSharedFileSysWow64); - SourceIsWildcard := IsWildcard(SourceWildcard); + SourceIsWildcard := not(foDownload in Options) and IsWildcard(SourceWildcard); if ExternalFile then begin if RecurseSubdirs then Include(Options, foRecurseSubDirsExternal); diff --git a/Projects/Src/Compression.SevenZipDLLDecoder.pas b/Projects/Src/Compression.SevenZipDLLDecoder.pas index 815d56c4..ea424e66 100644 --- a/Projects/Src/Compression.SevenZipDLLDecoder.pas +++ b/Projects/Src/Compression.SevenZipDLLDecoder.pas @@ -837,6 +837,7 @@ begin System.TMonitor.Exit(FLock); end; + { Also see Setup.Install THTTPDataReceiver.OnReceiveData } var Bytes := Progress - FPreviousProgress; while Bytes > 0 do begin var BytesToReport: Cardinal; @@ -847,7 +848,6 @@ begin FOnExtractToHandleProgress(BytesToReport); Dec(Bytes, BytesToReport); end; - FPreviousProgress := Progress; end; end; diff --git a/Projects/Src/IDE.ScintStylerInnoSetup.pas b/Projects/Src/IDE.ScintStylerInnoSetup.pas index a649c995..a541d857 100644 --- a/Projects/Src/IDE.ScintStylerInnoSetup.pas +++ b/Projects/Src/IDE.ScintStylerInnoSetup.pas @@ -242,14 +242,15 @@ const FilesSectionParameters: array of TScintRawString = [ 'AfterInstall', 'Attribs', 'BeforeInstall', 'Check', 'Components', 'CopyMode', - 'DestDir', 'DestName', 'Excludes', 'ExternalSize', 'ExtractArchivePassword', - 'Flags', 'FontInstall', 'ISSigAllowedKeys', 'Languages', 'MinVersion', - 'OnlyBelowVersion', 'Permissions', 'Source', 'StrongAssemblyName', 'Tasks' + 'DestDir', 'DestName', 'DownloadPassword', 'DownloadUserName', 'Excludes', + 'ExternalSize', 'ExtractArchivePassword', 'Flags', 'FontInstall', + 'ISSigAllowedKeys', 'Languages', 'MinVersion', 'OnlyBelowVersion', + 'Permissions', 'Source', 'StrongAssemblyName', 'Tasks' ]; FilesSectionFlags: array of TScintRawString = [ '32bit', '64bit', 'allowunsafefiles', 'comparetimestamp', 'confirmoverwrite', - 'createallsubdirs', 'deleteafterinstall', 'dontcopy', 'dontverifychecksum', + 'createallsubdirs', 'deleteafterinstall', 'dontcopy', 'dontverifychecksum', 'download', 'external', 'extractarchive', 'fontisnttruetype', 'gacinstall', 'ignoreversion', 'isreadme', 'issigverify', 'nocompression', 'noencryption', 'noregerror', 'onlyifdestfileexists', 'onlyifdoesntexist', 'overwritereadonly', 'promptifolder', diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index e08c5805..e799979b 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -26,9 +26,13 @@ procedure PerformInstall(var Succeeded: Boolean; const ChangesEnvironment, type TOnDownloadProgress = function(const Url, BaseName: string; const Progress, ProgressMax: Int64): Boolean of object; + TOnSimpleDownloadProgress = procedure(Bytes: Cardinal); procedure ExtractTemporaryFile(const BaseName: String); function ExtractTemporaryFiles(const Pattern: String): Integer; +function DownloadFile(const Url: String; const DestF: TFile; + const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; + const OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64; function DownloadTemporaryFile(const Url, BaseName, RequiredSHA256OfFile: String; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; const OnDownloadProgress: TOnDownloadProgress): Int64; @@ -73,11 +77,15 @@ begin WizardForm.FilenameLabel.Update; end; -procedure SetStatusLabelText(const S: String); +procedure SetStatusLabelText(const S: String; const CallUpdate: Boolean = True; + const ClearFilenameLabelText: Boolean = True); begin - WizardForm.StatusLabel.Caption := S; - WizardForm.StatusLabel.Update; - SetFilenameLabelText('', True); + if WizardForm.StatusLabel.Caption <> S then begin + WizardForm.StatusLabel.Caption := S; + WizardForm.StatusLabel.Update; + end; + if ClearFilenameLabelText then + SetFilenameLabelText('', True); end; procedure InstallMessageBoxCallback(const Flags: LongInt; const After: Boolean; @@ -1000,7 +1008,9 @@ var AExternalFileDate should not be set External : Opposite except AExternalFileDate still not set Ext. Archive: Same as external except AExternalFileDate set and - AExternalSourceFile should be set to ArchiveFindHandle as a string } + AExternalSourceFile should be set to ArchiveFindHandle as a string + Ext. Downl. : Same as external except + AExternalSourceFile should be set to an URL } procedure InstallFont(const Filename, FontName: String; const PerUserFont, AddToFontTableNow: Boolean; var WarnedPerUserFonts: Boolean); @@ -1243,6 +1253,10 @@ var end; { Update the filename label } + if foDownload in CurFile^.Options then + SetStatusLabelText(SetupMessages[msgStatusDownloadFiles], False) + else + SetStatusLabelText(SetupMessages[msgStatusExtractFiles], False); SetFilenameLabelText(DestFile, True); LogFmt('Dest filename: %s', [DestFile]); if DisableFsRedir <> InstallDefaultDisableFsRedir then begin @@ -1267,6 +1281,7 @@ var if DestFileExistedBefore then DeleteFlags := DeleteFlags or utDeleteFile_ExistedBeforeInstall; + var CurFileDateDidRead := True; { Set to False later if needed } if Assigned(CurFileLocation) then begin if floTimeStampInUTC in CurFileLocation^.Flags then CurFileDate := CurFileLocation^.SourceTimeStamp @@ -1276,11 +1291,15 @@ var end else if Assigned(AExternalFileDate) then begin CurFileDate := AExternalFileDate^; CurFileDateValid := CurFileDate.HasTime; - end else - CurFileDateValid := GetFileDateTime(DisableFsRedir, AExternalSourceFile, CurFileDate); + end else if not(foDownload in CurFile^.Options) then + CurFileDateValid := GetFileDateTime(DisableFsRedir, AExternalSourceFile, CurFileDate) + else begin + CurFileDateValid := False; + CurFileDateDidRead := False; + end; if CurFileDateValid then LogFmt('Time stamp of our file: %s', [FileTimeToStr(CurFileDate)]) - else + else if CurFileDateDidRead then Log('Time stamp of our file: (failed to read)'); if DestFileExists then begin @@ -1302,8 +1321,10 @@ var if not(foIgnoreVersion in CurFile^.Options) then begin AllowTimeStampComparison := False; { Read version info of file being installed } - if foExtractArchive in CurFile^.Options then - InternalError('Unexpected extractarchive flag'); + if foDownload in CurFile^.Options then + InternalError('Unexpected Download flag') + else if foExtractArchive in CurFile^.Options then + InternalError('Unexpected ExtractArchive flag'); if Assigned(CurFileLocation) then begin CurFileVersionInfoValid := floVersionInfoValid in CurFileLocation^.Flags; CurFileVersionInfo.MS := CurFileLocation^.FileVersionMS; @@ -1404,6 +1425,8 @@ var { Fall back to comparing time stamps if needed } if AllowTimeStampComparison and (foCompareTimeStamp in CurFile^.Options) then begin + if foDownload in CurFile^.Options then + InternalError('Unexpected Download flag'); if not CurFileDateValid or not ExistingFileDateValid then begin { If we failed to read one of the time stamps, do the safe thing and just skip the file } @@ -1542,6 +1565,11 @@ var LastOperation := SetupMessages[msgErrorExtracting]; ArchiveFindExtract(StrToInt(SourceFile), DestF, ExtractorProgressProc); end + else if foDownload in CurFile^.Options then begin + { Download a file } + LastOperation := SetupMessages[msgErrorDownloading]; + DownloadFile(SourceFile, DestF, foISSigVerify in CurFile^.Options, CurFile^.ISSigAllowedKeys, ExtractorProgressProc); + end else begin { Copy a duplicated non-external file, or an external file } SourceF := TFileRedir.Create(DisableFsRedir, SourceFile, fdOpenExisting, faRead, fsRead); @@ -2086,7 +2114,15 @@ var repeat SetProgress(ProgressBefore); ExpectedBytesLeft := CurFile^.ExternalSize; - if foExtractArchive in CurFile^.Options then + if foDownload in CurFile^.Options then begin + if not(foCustomDestName in CurFile^.Options) then + InternalError('Expected CustomDestName flag'); + { CurFile^.DestName now includes a a filename, see TSetupCompiler.EnumFilesProc.ProcessFileList } + ProcessFileEntry(CurFile, DisableFsRedir, SourceWildcard, ExpandConst(CurFile^.DestName), + nil, ExpectedBytesLeft, ConfirmOverwriteOverwriteAll, PromptIfOlderOverwriteAll, + WarnedPerUserFonts, nil); + FoundFiles := True; + end else if foExtractArchive in CurFile^.Options then FoundFiles := RecurseExternalArchiveCopyFiles(DisableFsRedir, SourceWildcard, CurFile^.ExtractArchivePassword, Excludes, CurFile, ExpectedBytesLeft, ConfirmOverwriteOverwriteAll, PromptIfOlderOverwriteAll, @@ -3598,6 +3634,7 @@ type private FBaseName, FUrl: String; FOnDownloadProgress: TOnDownloadProgress; + FOnSimpleDownloadProgress: TOnSimpleDownloadProgress; FAborted: Boolean; FProgress, FProgressMax: Int64; FLastReportedProgress, FLastReportedProgressMax: Int64; @@ -3605,6 +3642,7 @@ type property BaseName: String write FBaseName; property Url: String write FUrl; property OnDownloadProgress: TOnDownloadProgress write FOnDownloadProgress; + property OnSimpleDownloadProgress: TOnSimpleDownloadProgress write FOnSimpleDownloadProgress; property Aborted: Boolean read FAborted; property Progress: Int64 read FProgress; property ProgressMax: Int64 read FProgressMax; @@ -3633,6 +3671,19 @@ begin FLastReportedProgressMax := FProgressMax; end; end; + end else if Assigned(FOnSimpleDownloadProgress) then begin + { Also see Compression.SevenZipDLLDecoder TArchiveExtractToHandleCallback.HandleProgress } + var Bytes := Progress - FLastReportedProgress; + while Bytes > 0 do begin + var BytesToReport: Cardinal; + if Bytes > High(BytesToReport) then + BytesToReport := High(BytesToReport) + else + BytesToReport := Bytes; + FOnSimpleDownloadProgress(BytesToReport); + Dec(Bytes, BytesToReport); + end; + FLastReportedProgress := Progress; end; if not Abort and DownloadTemporaryFileOrExtractArchiveProcessMessages then @@ -3690,6 +3741,84 @@ begin Log('Download is not using basic authentication'); end; +function DownloadFile(const Url: String; const DestF: TFile; + const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; + const OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64; +var + DestFile: String; + HandleStream: THandleStream; + HTTPDataReceiver: THTTPDataReceiver; + HTTPClient: THTTPClient; + HTTPResponse: IHTTPResponse; + User, Pass, CleanUrl: String; + HasCredentials : Boolean; +begin + if Url = '' then + InternalError('DownloadFile: Invalid Url value'); + + LogFmt('Downloading file from %s', [MaskPasswordInURL(Url)]); + + HTTPDataReceiver := nil; + HTTPClient := nil; + HandleStream := nil; + + try + HasCredentials := GetCredentialsAndCleanUrl(URL, User, Pass, CleanUrl); + + { Setup downloader } + HTTPDataReceiver := THTTPDataReceiver.Create; + HTTPDataReceiver.Url := CleanUrl; + HTTPDataReceiver.OnSimpleDownloadProgress := OnSimpleDownloadProgress; + + HTTPClient := THTTPClient.Create; { http://docwiki.embarcadero.com/RADStudio/Rio/en/Using_an_HTTP_Client } + SetUserAgentAndSecureProtocols(HTTPClient); + HTTPClient.OnReceiveData := HTTPDataReceiver.OnReceiveData; + + { Download to specified handle } + HandleStream := THandleStream.Create(DestF.Handle); + if HasCredentials then begin + const Base64 = TBase64Encoding.Create(0); + try + HTTPClient.CustomHeaders['Authorization'] := 'Basic ' + Base64.Encode(User + ':' + Pass); + finally + Base64.Free; + end; + end; + HTTPResponse := HTTPClient.Get(CleanUrl, HandleStream); + Result := 0; { silence compiler } + if HTTPDataReceiver.Aborted then + Abort + else if (HTTPResponse.StatusCode < 200) or (HTTPResponse.StatusCode > 299) then + raise Exception.Create(Format('%d %s', [HTTPResponse.StatusCode, HTTPResponse.StatusText])) + else begin + { Download completed, get size and close it } + Result := HandleStream.Size; + FreeAndNil(HandleStream); + + { Check .issig if specified, otherwise check everything else we can check } + if ISSigVerify then begin + var ExpectedFileHash: TSHA256Digest; + DoISSigVerify(DestF, nil, DestFile, ISSigAllowedKeys, ExpectedFileHash); + const FileHash = GetSHA256OfFile(DestF); + if not SHA256DigestsEqual(FileHash, ExpectedFileHash) then + ISSigVerifyError(vseFileHashIncorrect, SetupMessages[msgSourceIsCorrupted]); + Log(ISSigVerificationSuccessfulLogMessage); + end else begin + if HTTPDataReceiver.ProgressMax > 0 then begin + if HTTPDataReceiver.Progress <> HTTPDataReceiver.ProgressMax then + raise Exception.Create(FmtSetupMessage(msgErrorProgress, [IntToStr(HTTPDataReceiver.Progress), IntToStr(HTTPDataReceiver.ProgressMax)])) + else if HTTPDataReceiver.ProgressMax <> Result then + raise Exception.Create(FmtSetupMessage(msgErrorFileSize, [IntToStr(HTTPDataReceiver.ProgressMax), IntToStr(Result)])); + end; + end; + end; + finally + HandleStream.Free; + HTTPClient.Free; + HTTPDataReceiver.Free; + end; +end; + function DownloadTemporaryFile(const Url, BaseName, RequiredSHA256OfFile: String; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; const OnDownloadProgress: TOnDownloadProgress): Int64; diff --git a/Projects/Src/Setup.MainFunc.pas b/Projects/Src/Setup.MainFunc.pas index 8e5b9038..4a369809 100644 --- a/Projects/Src/Setup.MainFunc.pas +++ b/Projects/Src/Setup.MainFunc.pas @@ -1811,7 +1811,7 @@ function EnumFiles(const EnumFilesProc: TEnumFilesProc; Result := True; if foCustomDestName in CurFile^.Options then - InternalError('Unexpected custom DestName'); + InternalError('Unexpected CustomDestName flag'); const DestDir = ExpandConst(CurFile^.DestName); var FindData: TWin32FindData; @@ -1864,21 +1864,29 @@ begin end else begin { External file } - SourceWildcard := ExpandConst(CurFile^.SourceFilename); - Excludes.DelimitedText := CurFile^.Excludes; - if foExtractArchive in CurFile^.Options then begin - try - if not RecurseExternalArchiveFiles(DisableFsRedir, SourceWildcard, - CurFile^.ExtractArchivePassword, Excludes, CurFile) then - Exit(False); - except on E: ESevenZipError do - { Ignore archive errors for now, will show up with proper UI during - installation } - end; - end else begin - if not RecurseExternalFiles(DisableFsRedir, PathExtractPath(SourceWildcard), '', - PathExtractName(SourceWildcard), IsWildcard(SourceWildcard), Excludes, CurFile) then + if foDownload in CurFile^.Options then begin + if not(foCustomDestName in CurFile^.Options) then + InternalError('Expected CustomDestName flag'); + { CurFile^.DestName now includes a a filename, see TSetupCompiler.EnumFilesProc.ProcessFileList } + if not EnumFilesProc(DisableFsRedir, ExpandConst(CurFile^.DestName), Param) then Exit(False); + end else begin + SourceWildcard := ExpandConst(CurFile^.SourceFilename); + Excludes.DelimitedText := CurFile^.Excludes; + if foExtractArchive in CurFile^.Options then begin + try + if not RecurseExternalArchiveFiles(DisableFsRedir, SourceWildcard, + CurFile^.ExtractArchivePassword, Excludes, CurFile) then + Exit(False); + except on E: ESevenZipError do + { Ignore archive errors for now, will show up with proper UI during + installation } + end; + end else begin + if not RecurseExternalFiles(DisableFsRedir, PathExtractPath(SourceWildcard), '', + PathExtractName(SourceWildcard), IsWildcard(SourceWildcard), Excludes, CurFile) then + Exit(False); + end; end; end; end; @@ -3516,6 +3524,8 @@ begin Inc6464(MinimumSpace, PSetupFileLocationEntry(Entries[seFileLocation][LocationEntry])^.OriginalSize) end else begin if not(foExternalSizePreset in Options) then begin + if foDownload in Options then + InternalError('Unexpected download flag'); try LExcludes.DelimitedText := Excludes; if foExtractArchive in Options then begin diff --git a/Projects/Src/Shared.SetupMessageIDs.pas b/Projects/Src/Shared.SetupMessageIDs.pas index 00249c0a..b7420aa2 100644 --- a/Projects/Src/Shared.SetupMessageIDs.pas +++ b/Projects/Src/Shared.SetupMessageIDs.pas @@ -87,6 +87,7 @@ type msgErrorCreatingTemp, msgErrorDownloadAborted, msgErrorDownloadFailed, + msgErrorDownloading, msgErrorDownloadSizeFailed, msgErrorExecutingProgram, msgErrorExtracting, @@ -235,6 +236,7 @@ type msgStatusCreateIcons, msgStatusCreateIniEntries, msgStatusCreateRegistryEntries, + msgStatusDownloadFiles, msgStatusExtractFiles, msgStatusRegisterFiles, msgStatusRestartingApplications, diff --git a/Projects/Src/Shared.Struct.pas b/Projects/Src/Shared.Struct.pas index 03b42daf..84605683 100644 --- a/Projects/Src/Shared.Struct.pas +++ b/Projects/Src/Shared.Struct.pas @@ -227,14 +227,14 @@ type PublicX, PublicY, RuntimeID: String; end; const - SetupFileEntryStrings = 12; + SetupFileEntryStrings = 14; SetupFileEntryAnsiStrings = 1; type PSetupFileEntry = ^TSetupFileEntry; TSetupFileEntry = packed record SourceFilename, DestName, InstallFontName, StrongAssemblyName, Components, Tasks, Languages, Check, AfterInstall, BeforeInstall, Excludes, - ExtractArchivePassword: String; + DownloadUserName, DownloadPassword, ExtractArchivePassword: String; ISSigAllowedKeys: AnsiString; MinVersion, OnlyBelowVersion: TSetupVersionData; LocationEntry: Integer; @@ -251,7 +251,8 @@ type foRecurseSubDirsExternal, foReplaceSameVersionIfContentsDiffer, foDontVerifyChecksum, foUninsNoSharedFilePrompt, foCreateAllSubDirs, fo32Bit, fo64Bit, foExternalSizePreset, foSetNTFSCompression, - foUnsetNTFSCompression, foGacInstall, foISSigVerify, foExtractArchive); + foUnsetNTFSCompression, foGacInstall, foISSigVerify, foDownload, + foExtractArchive); FileType: (ftUserFile, ftUninstExe); end; const From 20e10bb065cba603dad917ae72148d929aa15283 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Sun, 8 Jun 2025 16:15:49 +0200 Subject: [PATCH 02/19] Improve progress/abort handling. --- Projects/Src/Setup.Install.pas | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index e799979b..7ecf556d 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -3671,6 +3671,12 @@ begin FLastReportedProgressMax := FProgressMax; end; end; + + if not Abort and DownloadTemporaryFileOrExtractArchiveProcessMessages then + Application.ProcessMessages; + + if Abort then + FAborted := True end else if Assigned(FOnSimpleDownloadProgress) then begin { Also see Compression.SevenZipDLLDecoder TArchiveExtractToHandleCallback.HandleProgress } var Bytes := Progress - FLastReportedProgress; @@ -3680,17 +3686,20 @@ begin BytesToReport := High(BytesToReport) else BytesToReport := Bytes; - FOnSimpleDownloadProgress(BytesToReport); + try + FOnSimpleDownloadProgress(BytesToReport); + except + if ExceptObject is EAbort then begin + Abort := True; + FAborted := True; + Break; + end else + raise; + end; Dec(Bytes, BytesToReport); end; FLastReportedProgress := Progress; end; - - if not Abort and DownloadTemporaryFileOrExtractArchiveProcessMessages then - Application.ProcessMessages; - - if Abort then - FAborted := True end; procedure SetUserAgentAndSecureProtocols(const AHTTPClient: THTTPClient); From 0b21379f6351a65aaba10761a4c2235c41380384 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Sun, 8 Jun 2025 16:51:16 +0200 Subject: [PATCH 03/19] Some documentation. --- ISHelp/isetup.xml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/ISHelp/isetup.xml b/ISHelp/isetup.xml index 583e603d..88bcf5dc 100644 --- a/ISHelp/isetup.xml +++ b/ISHelp/isetup.xml @@ -1554,7 +1554,8 @@ Source: "README.TXT"; DestDir: "{app}"; Flags: isreadme
The name of the source file. The compiler will prepend the path of your installation's source directory if you do not specify a fully qualified pathname.
This can be a wildcard to specify a group of files in a single entry. When a wildcard is used, all files matching it use the same options.
-When the flag external is specified, Source must be the full pathname of an existing file (or wildcard) on the distribution media or the user's system (e.g. "{src}\license.ini").
+When the flag external is specified but the flag download is not, Source must be the full pathname of an existing file (or wildcard) on the distribution media or the user's system (e.g. "{src}\license.ini").
+When the flag external is specified and the flag download is also, Source must be the URL of the file to download.
Constants may only be used when the external flag is specified, because the compiler does not do any constant translating itself.
@@ -1665,6 +1666,16 @@ ExternalSize: 1048576; Flags: externalThis parameter is ignored if the extractarchive flag isn't also specified.
+ +Specifies the basic authentication username to use for the file download.
+This parameter is ignored if the download flag isn't also specified.
+ + + +Specifies the basic authentication password to use for the file download. Please be aware that this password is stored in an unencrypted form in the resulting Setup file(s), even if you have enabled encryption (using the [Setup] section directive Encryption).
+This parameter is ignored if the download flag isn't also specified.
+ +This parameter is a set of extra options. Multiple options may be used by separating them by spaces. The following options are supported:
@@ -1708,14 +1719,23 @@ Instructs Setup to proceed to comparing time stamps (last write/modified time) iPrevents Setup from verifying the file checksum after extraction. Use this flag on files you wish to modify while already compiled into Setup.
Must be combined with nocompression.
++ This flag instructs Setup not to copy an existing file, but instead to download it. Optionally use the DownloadUserName and DownloadPassword parameters to specify a basic authentication username and password.
+This flag must be combined with the DestName and ExternalSize parameters.
+This flag also must be combined with the external and ignoreversion flags, meaning it should only be used on files private to your application, never on shared system files.
+This flag cannot be combined with the comparetimestamp, extractarchive, and recursesubdirs flags.
+Supports HTTPS (but not expired or self-signed certificates) and HTTP. Redirects are automatically followed and proxy settings are automatically used. Safe to use from services.
+This flag instructs Inno Setup not to statically compile the file specified by the Source parameter into the installation files, but instead to copy from an existing file on the distribution media or the user's system. See the Source parameter description for more information.
+When combined with the download or extractarchive flags, Setup does not copy the file, but instead downloads or extracts it.
- This flag instructs Inno Setup not to copy an existing archive file, but instead to extract it. Optionally use the ExtractArchivePassword parameter to specify a password.
+This flag instructs Setup not to copy an existing archive file, but instead to extract it. Optionally use the ExtractArchivePassword parameter to specify a password.
The supported archive formats, beyond .7z, and the support for password-protected archives, depend on the ArchiveExtraction [Setup] section directive, that must not be set to basic.
This flag must be combined with the external and ignoreversion flags, meaning it should only be used on files private to your application, never on shared system files.
This flag is usually combined with the recursesubdirs and createallsubdirs flags.
+This flag cannot be combined with download.
Using a solid archive is not recommended; extraction performance may degrade depending on the solid block size.
@@ -1814,6 +1834,7 @@ Instructs Setup to proceed to comparing time stamps (last write/modified time) i This flag instructs the compiler -- or Setup, if the external flag is also used -- to silently skip over the entry if the source file does not exist, instead of displaying an error message.
+When combined with the download flag, this flag instructs Setup to skip if the download fails for any reason.
When solid compression is enabled, this flag instructs the compiler to finalize the current compression stream and begin a new one before compressing the file(s) matched by Source. This allows Setup to seek to the file instantly without having to decompress any preceding files first. May be useful in a large, multi-component installation if you find too much time is being spent decompressing files belonging to components that weren't selected.
From 5b4385bfa7b12a09aa58ded58fe7a7599941d60f Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Sun, 8 Jun 2025 18:39:59 +0200 Subject: [PATCH 04/19] Make DownloadUserName and DownloadPassword work. Todo: test. --- Projects/Src/Setup.Install.pas | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index fc63d92e..6a05bbb6 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -30,8 +30,8 @@ type procedure ExtractTemporaryFile(const BaseName: String); function ExtractTemporaryFiles(const Pattern: String): Integer; -function DownloadFile(const Url: String; const DestF: TFile; - const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; +function DownloadFile(const Url, CustomUserName, CustomPassword: String; + const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; const OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64; function DownloadTemporaryFile(const Url, BaseName, RequiredSHA256OfFile: String; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; @@ -1569,7 +1569,8 @@ var else if foDownload in CurFile^.Options then begin { Download a file } LastOperation := SetupMessages[msgErrorDownloading]; - DownloadFile(SourceFile, DestF, foISSigVerify in CurFile^.Options, CurFile^.ISSigAllowedKeys, ExtractorProgressProc); + DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword, + DestF, foISSigVerify in CurFile^.Options, CurFile^.ISSigAllowedKeys, ExtractorProgressProc); end else begin { Copy a duplicated non-external file, or an external file } @@ -3765,8 +3766,8 @@ begin end; end; -function DownloadFile(const Url: String; const DestF: TFile; - const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; +function DownloadFile(const Url, CustomUserName, CustomPassword: String; + const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; const OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64; var DestFile: String; @@ -3787,7 +3788,8 @@ begin HandleStream := nil; try - HasCredentials := GetCredentialsAndCleanUrl(URL, '', '', User, Pass, CleanUrl); + HasCredentials := GetCredentialsAndCleanUrl(URL, + CustomUserName, CustomPassword, User, Pass, CleanUrl); { Setup downloader } HTTPDataReceiver := THTTPDataReceiver.Create; From 813d024dd40a54b84671f63a70d6f7ec595695f0 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Sun, 8 Jun 2025 20:27:45 +0200 Subject: [PATCH 05/19] Add download+issigverify support. This finishes the branch, only whatsnew todo + making a new fresh messages commit + test username/password support. --- Components/ISSigFunc.pas | 1 + ISHelp/isetup.xml | 6 +++++ ISHelp/isxfunc.xml | 2 +- Projects/Src/Compiler.SetupCompiler.pas | 7 +++++- Projects/Src/Setup.Install.pas | 32 ++++++++++++++++++++----- Projects/Src/Setup.MainFunc.pas | 2 ++ Projects/Src/Shared.Struct.pas | 4 ++-- 7 files changed, 44 insertions(+), 10 deletions(-) diff --git a/Components/ISSigFunc.pas b/Components/ISSigFunc.pas index e80c1ae5..04b2acb2 100644 --- a/Components/ISSigFunc.pas +++ b/Components/ISSigFunc.pas @@ -70,6 +70,7 @@ function ISSigCalcStreamHash(const AStream: TStream): TSHA256Digest; var ISSigExt: String = '.issig'; + ISSigEstimatedSize: Integer = 330; implementation diff --git a/ISHelp/isetup.xml b/ISHelp/isetup.xml index 925cabb8..f79a8f15 100644 --- a/ISHelp/isetup.xml +++ b/ISHelp/isetup.xml @@ -1668,6 +1668,12 @@ ExternalSize: 1048576; Flags: externalThis parameter is ignored if the extractarchive flag isn't also specified.
+ +Specifies the URL of the .issig signature file which should be downloaded. This file is used to verify the file downloaded from the URL specified by the Source parameter.
+This parameter is ignored if the download and issigverify flags aren't both also specified.
+If this parameter is not set but both these flags are used, Setup will instead append ".issig" (without quotes) to the path portion of the URL specified by the Source parameter. It will then use the result as the URL to download the .issig signature file from.
+ +Specifies the basic authentication username to use for the file download.
This parameter is ignored if the download flag isn't also specified.
diff --git a/ISHelp/isxfunc.xml b/ISHelp/isxfunc.xml index eed631d7..0504edf1 100644 --- a/ISHelp/isxfunc.xml +++ b/ISHelp/isxfunc.xml @@ -1862,7 +1862,7 @@ end;
Like DownloadTemporaryFile, but downloads an .issig signature file first from the specified second URL and uses it to verify the main file downloaded from the first URL.
-If the second URL is an empty string, Setup will instead append ".issig" (without quotes) to the path portion of the first URL and use the result as the URL to download the .issig signature file from.
+If the second URL is an empty string, Setup will instead append ".issig" (without quotes) to the path portion of the first URL. It will then use the result as the URL to download the .issig signature file from.
Verification uses the specified allowed keys, looked up using [ISSigKeys] section parameter RuntimeID. To allow all keys set AllowedKeysRuntimeIDs to nil.
An exception will be raised if there was an error. Otherwise, returns the number of bytes downloaded for the main file from the first URL. Returns 0 if the main file was already downloaded and still verified.
DownloadTemporaryFile
diff --git a/Projects/Src/Compiler.SetupCompiler.pas b/Projects/Src/Compiler.SetupCompiler.pas
index 536d0d14..4f2e76b1 100644
--- a/Projects/Src/Compiler.SetupCompiler.pas
+++ b/Projects/Src/Compiler.SetupCompiler.pas
@@ -4670,7 +4670,7 @@ type
paPermissions, paFontInstall, paExcludes, paExternalSize, paExtractArchivePassword,
paStrongAssemblyName, paISSigAllowedKeys, paComponents, paTasks, paLanguages,
paCheck, paBeforeInstall, paAfterInstall, paMinVersion, paOnlyBelowVersion,
- paDownloadUserName, paDownloadPassword);
+ paDownloadISSigSource, paDownloadUserName, paDownloadPassword);
const
ParamFilesSource = 'Source';
ParamFilesDestDir = 'DestDir';
@@ -4684,6 +4684,7 @@ const
ParamFilesExtractArchivePassword = 'ExtractArchivePassword';
ParamFilesStrongAssemblyName = 'StrongAssemblyName';
ParamFilesISSigAllowedKeys = 'ISSigAllowedKeys';
+ ParamFilesDownloadISSigSource = 'DownloadISSigSource';
ParamFilesDownloadUserName = 'DownloadUserName';
ParamFilesDownloadPassword = 'DownloadPassword';
ParamInfo: array[TParam] of TParamInfo = (
@@ -4700,6 +4701,7 @@ const
(Name: ParamFilesExtractArchivePassword; Flags: []),
(Name: ParamFilesStrongAssemblyName; Flags: [piNoEmpty]),
(Name: ParamFilesISSigAllowedKeys; Flags: [piNoEmpty]),
+ (Name: ParamFilesDownloadISSigSource; Flags: []),
(Name: ParamFilesDownloadUserName; Flags: [piNoEmpty]),
(Name: ParamFilesDownloadPassword; Flags: [piNoEmpty]),
(Name: ParamCommonComponents; Flags: []),
@@ -5350,6 +5352,9 @@ begin
Include(Options, foExternalSizePreset);
end;
+ { DownloadISSigSource }
+ DownloadISSigSource := Values[paDownloadISSigSource].Data;
+
{ DownloadUserName }
DownloadUserName := Values[paDownloadUserName].Data;
diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas
index 6a05bbb6..b8550959 100644
--- a/Projects/Src/Setup.Install.pas
+++ b/Projects/Src/Setup.Install.pas
@@ -32,6 +32,7 @@ procedure ExtractTemporaryFile(const BaseName: String);
function ExtractTemporaryFiles(const Pattern: String): Integer;
function DownloadFile(const Url, CustomUserName, CustomPassword: String;
const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString;
+ const ISSigSourceFilename: String;
const OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64;
function DownloadTemporaryFile(const Url, BaseName, RequiredSHA256OfFile: String;
const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString;
@@ -1561,16 +1562,35 @@ var
not (foDontVerifyChecksum in CurFile^.Options));
end
else if foExtractArchive in CurFile^.Options then begin
- { Extract a file from archive. Note: foISSigVerify for archive has
+ { Extract a file from archive. Note: ISSigVerify for archive has
already been handled by RecurseExternalArchiveCopyFiles. }
LastOperation := SetupMessages[msgErrorExtracting];
ArchiveFindExtract(StrToInt(SourceFile), DestF, ExtractorProgressProc);
end
else if foDownload in CurFile^.Options then begin
- { Download a file }
+ { Download a file with or without ISSigVerify. Note: estimate of
+ extra .issig size has already been added to CurFile's ExternalSize. }
LastOperation := SetupMessages[msgErrorDownloading];
- DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword,
- DestF, foISSigVerify in CurFile^.Options, CurFile^.ISSigAllowedKeys, ExtractorProgressProc);
+ if foISSigVerify in CurFile^.Options then begin
+ const ISSigTempFile = TempFile + ISSigExt;
+ const ISSigDestF = TFileRedir.Create(DisableFsRedir, ISSigTempFile, fdCreateAlways, faReadWrite, fsNone);
+ try
+ { Download the .issig file }
+ const ISSigUrl = GetISSigUrl(SourceFile, CurFile^.DownloadISSigSource);
+ DownloadFile(ISSigUrl, CurFile^.DownloadUserName, CurFile^.DownloadPassword,
+ ISSigDestF, False, '', '', ExtractorProgressProc);
+ FreeAndNil(ISSigDestF);
+ { Download and verify the actual file }
+ DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword,
+ DestF, True, CurFile^.ISSigAllowedKeys, TempFile, ExtractorProgressProc);
+ finally
+ ISSigDestF.Free;
+ { Delete the .issig file }
+ DeleteFileRedir(DisableFsRedir, ISSigTempFile);
+ end;
+ end else
+ DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword,
+ DestF, False, '', '', ExtractorProgressProc);
end
else begin
{ Copy a duplicated non-external file, or an external file }
@@ -3768,9 +3788,9 @@ end;
function DownloadFile(const Url, CustomUserName, CustomPassword: String;
const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString;
+ const ISSigSourceFilename: String;
const OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64;
var
- DestFile: String;
HandleStream: THandleStream;
HTTPDataReceiver: THTTPDataReceiver;
HTTPClient: THTTPClient;
@@ -3824,7 +3844,7 @@ begin
{ Check .issig if specified, otherwise check everything else we can check }
if ISSigVerify then begin
var ExpectedFileHash: TSHA256Digest;
- DoISSigVerify(DestF, nil, DestFile, ISSigAllowedKeys, ExpectedFileHash);
+ DoISSigVerify(DestF, nil, ISSigSourceFilename, ISSigAllowedKeys, ExpectedFileHash);
const FileHash = GetSHA256OfFile(DestF);
if not SHA256DigestsEqual(FileHash, ExpectedFileHash) then
ISSigVerifyError(vseFileHashIncorrect, SetupMessages[msgSourceIsCorrupted]);
diff --git a/Projects/Src/Setup.MainFunc.pas b/Projects/Src/Setup.MainFunc.pas
index 4a369809..ea7a0122 100644
--- a/Projects/Src/Setup.MainFunc.pas
+++ b/Projects/Src/Setup.MainFunc.pas
@@ -3550,6 +3550,8 @@ begin
(ESevenZipError). Also see EnumFiles. }
end;
end;
+ if (foDownload in Options) and (foISSigVerify in Options) then
+ Inc6464(ExternalSize, Integer64(UInt64(ISSigEstimatedSize)));
if Components = '' then { no types or a file that doesn't belong to any component }
if (Tasks = '') and (Check = '') then {don't count tasks or scripted entries}
Inc6464(MinimumSpace, ExternalSize);
diff --git a/Projects/Src/Shared.Struct.pas b/Projects/Src/Shared.Struct.pas
index 84605683..4e2e17de 100644
--- a/Projects/Src/Shared.Struct.pas
+++ b/Projects/Src/Shared.Struct.pas
@@ -227,14 +227,14 @@ type
PublicX, PublicY, RuntimeID: String;
end;
const
- SetupFileEntryStrings = 14;
+ SetupFileEntryStrings = 15;
SetupFileEntryAnsiStrings = 1;
type
PSetupFileEntry = ^TSetupFileEntry;
TSetupFileEntry = packed record
SourceFilename, DestName, InstallFontName, StrongAssemblyName, Components,
Tasks, Languages, Check, AfterInstall, BeforeInstall, Excludes,
- DownloadUserName, DownloadPassword, ExtractArchivePassword: String;
+ DownloadISSigSource, DownloadUserName, DownloadPassword, ExtractArchivePassword: String;
ISSigAllowedKeys: AnsiString;
MinVersion, OnlyBelowVersion: TSetupVersionData;
LocationEntry: Integer;
From 4e8c8962323a1f4576cb0f15e469f22c65533b6d Mon Sep 17 00:00:00 2001
From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com>
Date: Sun, 8 Jun 2025 20:45:50 +0200
Subject: [PATCH 06/19] Should work on the example as well. Keep the original
as CodeDownloadFiles2.iss. Should add a DownloadRequiredSha256 parameter for
the ISCrypt.dll example + should remove the archive from it for now.
So todo:
-Add DownloadRequiredSha256 parameter. (Or DownloadRequiredSHA256OfFile?)
-Update whatsnew.
-Update CodeDownloadFiles references and add new ones.
-Make a new fresh messages commit.
-Test username/password support.
---
Examples/CodeDownloadFiles.iss | 14 ++---
Examples/CodeDownloadFiles2.iss | 101 ++++++++++++++++++++++++++++++++
setup.iss | 1 +
3 files changed, 108 insertions(+), 8 deletions(-)
create mode 100644 Examples/CodeDownloadFiles2.iss
diff --git a/Examples/CodeDownloadFiles.iss b/Examples/CodeDownloadFiles.iss
index ca3fcabd..65cde7a2 100644
--- a/Examples/CodeDownloadFiles.iss
+++ b/Examples/CodeDownloadFiles.iss
@@ -1,8 +1,7 @@
; -- CodeDownloadFiles.iss --
;
-; This script shows how the CreateDownloadPage support function can be used to
-; download temporary files and archives while showing the download and extraction
-; progress to the user.
+; This script shows how the [Files] section can be used to download files and
+; archives while showing the download and extraction progress to the user.
;
; To verify the downloaded files, this script shows two methods:
; -For innosetup-latest.exe and MyProg-ExtraReadmes.7z: using Inno Setup
@@ -36,9 +35,11 @@ Name: "mykey"; RuntimeID: "def02"; \
Source: "MyProg.exe"; DestDir: "{app}"
Source: "MyProg.chm"; DestDir: "{app}"
Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
-; These files will be downloaded. If you include flag issigverify here the file will be verified
+; These files will be downloaded using [Files] only
+Source: "https://jrsoftware.org/download.php/is.exe?dontcount=1"; DestName: "innosetup-latest.exe"; DestDir: "{app}"; \
+ ExternalSize: 7000000; Flags: external download ignoreversion issigverify
+; These files will be downloaded by [Code]. If you include flag issigverify here the file will be verified
; a second time while copying. Verification while copying is efficient, except for archives.
-Source: "{tmp}\innosetup-latest.exe"; DestDir: "{app}"; Flags: external ignoreversion issigverify
Source: "{tmp}\MyProg-ExtraReadmes.7z"; DestDir: "{app}"; Flags: external extractarchive recursesubdirs ignoreversion
Source: "{tmp}\ISCrypt.dll"; DestDir: "{app}"; Flags: external ignoreversion
@@ -71,9 +72,6 @@ begin
if CurPageID = wpReady then begin
DownloadPage.Clear;
// Use AddEx or AddExWithISSigVerify to specify a username and password
- DownloadPage.AddWithISSigVerify(
- 'https://jrsoftware.org/download.php/is.exe?dontcount=1', '',
- 'innosetup-latest.exe', AllowedKeysRuntimeIDs);
DownloadPage.AddWithISSigVerify(
'https://jrsoftware.org/download.php/myprog-extrareadmes.7z', '',
'MyProg-ExtraReadmes.7z', AllowedKeysRuntimeIDs);
diff --git a/Examples/CodeDownloadFiles2.iss b/Examples/CodeDownloadFiles2.iss
new file mode 100644
index 00000000..cf3966ec
--- /dev/null
+++ b/Examples/CodeDownloadFiles2.iss
@@ -0,0 +1,101 @@
+; -- CodeDownloadFiles2.iss --
+;
+; This script shows how the CreateDownloadPage support function can be used to
+; download files and archives while showing the download and extraction
+; progress to the user.
+;
+; To verify the downloaded files, this script shows two methods:
+; -For innosetup-latest.exe and MyProg-ExtraReadmes.7z: using Inno Setup
+; Signature Tool, the [ISSigKeys] section, and the AddWithISSigVerify support
+; function
+; -For iscrypt.dll: using a simple SHA256 check
+; Using the Inno Setup Signature Tool has the benefit that the script does not
+; need to be changed when the downloaded file changes, so any installers built
+; will also keep working
+
+[Setup]
+AppName=My Program
+AppVersion=1.5
+WizardStyle=modern
+DefaultDirName={autopf}\My Program
+DefaultGroupName=My Program
+UninstallDisplayIcon={app}\MyProg.exe
+OutputDir=userdocs:Inno Setup Examples Output
+;Use "ArchiveExtraction=enhanced" if your archive has a password
+;Use "ArchiveExtraction=full" if your archive is not a .7z file but for example a .zip file
+ArchiveExtraction=enhanced/nopassword
+
+[ISSigKeys]
+Name: "mykey"; RuntimeID: "def02"; \
+ KeyID: "def020edee3c4835fd54d85eff8b66d4d899b22a777353ca4a114b652e5e7a28"; \
+ PublicX: "515dc7d6c16d4a46272ceb3d158c5630a96466ab4d948e72c2029d737c823097"; \
+ PublicY: "f3c21f6b5156c52a35f6f28016ee3e31a3ded60c325b81fb7b1f88c221081a61"
+
+[Files]
+; Place any regular files here
+Source: "MyProg.exe"; DestDir: "{app}"
+Source: "MyProg.chm"; DestDir: "{app}"
+Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
+; These files will be downloaded. If you include flag issigverify here the file will be verified
+; a second time while copying. Verification while copying is efficient, except for archives.
+Source: "{tmp}\innosetup-latest.exe"; DestDir: "{app}"; Flags: external ignoreversion issigverify
+Source: "{tmp}\MyProg-ExtraReadmes.7z"; DestDir: "{app}"; Flags: external extractarchive recursesubdirs ignoreversion
+Source: "{tmp}\ISCrypt.dll"; DestDir: "{app}"; Flags: external ignoreversion
+
+[Icons]
+Name: "{group}\My Program"; Filename: "{app}\MyProg.exe"
+
+[Code]
+var
+ DownloadPage: TDownloadWizardPage;
+ AllowedKeysRuntimeIDs: TStringList;
+
+procedure InitializeWizard;
+begin
+ DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil);
+ DownloadPage.ShowBaseNameInsteadOfUrl := True;
+
+ // To allow all keys you can also just pass nil instead of this list to AddWithISSigVerify
+ AllowedKeysRuntimeIDs := TStringList.Create;
+ AllowedKeysRuntimeIDs.Add('def02');
+end;
+
+procedure DeinitializeSetup;
+begin
+ if AllowedKeysRuntimeIDs <> nil then
+ AllowedKeysRuntimeIDs.Free;
+end;
+
+function NextButtonClick(CurPageID: Integer): Boolean;
+begin
+ if CurPageID = wpReady then begin
+ DownloadPage.Clear;
+ // Use AddEx or AddExWithISSigVerify to specify a username and password
+ DownloadPage.AddWithISSigVerify(
+ 'https://jrsoftware.org/download.php/is.exe?dontcount=1', '',
+ 'innosetup-latest.exe', AllowedKeysRuntimeIDs);
+ DownloadPage.AddWithISSigVerify(
+ 'https://jrsoftware.org/download.php/myprog-extrareadmes.7z', '',
+ 'MyProg-ExtraReadmes.7z', AllowedKeysRuntimeIDs);
+ DownloadPage.Add(
+ 'https://jrsoftware.org/download.php/iscrypt.dll?dontcount=1',
+ 'ISCrypt.dll', '2f6294f9aa09f59a574b5dcd33be54e16b39377984f3d5658cda44950fa0f8fc');
+ DownloadPage.Show;
+ try
+ try
+ // Downloads the files to {tmp}
+ DownloadPage.Download;
+ Result := True;
+ except
+ if DownloadPage.AbortedByUser then
+ Log('Aborted by user.')
+ else
+ SuppressibleMsgBox(AddPeriod(GetExceptionMessage), mbCriticalError, MB_OK, IDOK);
+ Result := False;
+ end;
+ finally
+ DownloadPage.Hide;
+ end;
+ end else
+ Result := True;
+end;
\ No newline at end of file
diff --git a/setup.iss b/setup.iss
index 4f4977e4..29c3286f 100644
--- a/setup.iss
+++ b/setup.iss
@@ -176,6 +176,7 @@ Source: "Examples\CodeClasses.iss"; DestDir: "{app}\Examples"; Flags: ignorevers
Source: "Examples\CodeDlg.iss"; DestDir: "{app}\Examples"; Flags: ignoreversion touch
Source: "Examples\CodeDll.iss"; DestDir: "{app}\Examples"; Flags: ignoreversion touch
Source: "Examples\CodeDownloadFiles.iss"; DestDir: "{app}\Examples"; Flags: ignoreversion touch
+Source: "Examples\CodeDownloadFiles2.iss"; DestDir: "{app}\Examples"; Flags: ignoreversion touch
Source: "Examples\CodeExample1.iss"; DestDir: "{app}\Examples"; Flags: ignoreversion touch
Source: "Examples\CodePrepareToInstall.iss"; DestDir: "{app}\Examples"; Flags: ignoreversion touch
Source: "Examples\Components.iss"; DestDir: "{app}\Examples"; Flags: ignoreversion touch
From b0020d0f9f7efa8bd526d8f2952bc894bf01acd9 Mon Sep 17 00:00:00 2001
From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com>
Date: Sun, 8 Jun 2025 22:22:21 +0200
Subject: [PATCH 07/19] Use digit separators.
---
Examples/CodeDownloadFiles.iss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Examples/CodeDownloadFiles.iss b/Examples/CodeDownloadFiles.iss
index 65cde7a2..81f47178 100644
--- a/Examples/CodeDownloadFiles.iss
+++ b/Examples/CodeDownloadFiles.iss
@@ -37,7 +37,7 @@ Source: "MyProg.chm"; DestDir: "{app}"
Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
; These files will be downloaded using [Files] only
Source: "https://jrsoftware.org/download.php/is.exe?dontcount=1"; DestName: "innosetup-latest.exe"; DestDir: "{app}"; \
- ExternalSize: 7000000; Flags: external download ignoreversion issigverify
+ ExternalSize: 7_000_000; Flags: external download ignoreversion issigverify
; These files will be downloaded by [Code]. If you include flag issigverify here the file will be verified
; a second time while copying. Verification while copying is efficient, except for archives.
Source: "{tmp}\MyProg-ExtraReadmes.7z"; DestDir: "{app}"; Flags: external extractarchive recursesubdirs ignoreversion
From 5d1fe8ef71c15a0d6fe25160d784da7da5cc5d42 Mon Sep 17 00:00:00 2001
From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com>
Date: Mon, 9 Jun 2025 10:01:44 +0200
Subject: [PATCH 08/19] GetISSigUrl: don't decode.
---
Projects/Src/Setup.Install.pas | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas
index b744ca6d..73da8ddd 100644
--- a/Projects/Src/Setup.Install.pas
+++ b/Projects/Src/Setup.Install.pas
@@ -3782,7 +3782,7 @@ begin
Result := ISSigUrl
else begin
const Uri = TUri.Create(Url); { This is a record so no need to free }
- Uri.Path := TNetEncoding.URL.Decode(Uri.Path) + ISSigExt;
+ Uri.Path := Uri.Path + ISSigExt;
Result := Uri.ToString;
end;
end;
From 4b4ea3c8f60a4ce3b5ad3c54ef4999027a93e287 Mon Sep 17 00:00:00 2001
From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com>
Date: Mon, 9 Jun 2025 19:23:00 +0200
Subject: [PATCH 09/19] Don't allow download + skipifsourcedoesntexist.
---
ISHelp/isetup.xml | 3 +--
Projects/Src/Compiler.SetupCompiler.pas | 2 ++
Projects/Src/Setup.Install.pas | 2 ++
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/ISHelp/isetup.xml b/ISHelp/isetup.xml
index 120cf920..85fd7e64 100644
--- a/ISHelp/isetup.xml
+++ b/ISHelp/isetup.xml
@@ -1733,7 +1733,7 @@ Instructs Setup to proceed to comparing time stamps (last write/modified time) i
This flag instructs Setup not to copy an existing file, but instead to download it. Optionally use the DownloadUserName and DownloadPassword parameters to specify a basic authentication username and password.
This flag must be combined with the DestName and ExternalSize parameters.
This flag also must be combined with the external and ignoreversion flags, meaning it should only be used on files private to your application, never on shared system files.
-This flag cannot be combined with the comparetimestamp, extractarchive, and recursesubdirs flags.
+This flag cannot be combined with the comparetimestamp, extractarchive, recursesubdirs, and skipifsourcedoesntexist flags.
Supports HTTPS (but not expired or self-signed certificates) and HTTP. Redirects are automatically followed and proxy settings are automatically used. Safe to use from services.
This flag instructs the compiler -- or Setup, if the external flag is also used -- to silently skip over the entry if the source file does not exist, instead of displaying an error message.
-When combined with the download flag, this flag instructs Setup to skip if the download or extraction fails for any reason.
When solid compression is enabled, this flag instructs the compiler to finalize the current compression stream and begin a new one before compressing the file(s) matched by Source. This allows Setup to seek to the file instantly without having to decompress any preceding files first. May be useful in a large, multi-component installation if you find too much time is being spent decompressing files belonging to components that weren't selected.
diff --git a/Projects/Src/Compiler.SetupCompiler.pas b/Projects/Src/Compiler.SetupCompiler.pas index 1795f68d..1764c2c3 100644 --- a/Projects/Src/Compiler.SetupCompiler.pas +++ b/Projects/Src/Compiler.SetupCompiler.pas @@ -5457,6 +5457,8 @@ begin AbortCompileFmt(SCompilerParamErrorBadCombo2, [ParamCommonFlags, 'download', 'extractarchive']) else if foCompareTimeStamp in Options then AbortCompileFmt(SCompilerParamErrorBadCombo2, [ParamCommonFlags, 'download', 'comparetimestamp']) + else if foSkipIfSourceDoesntExist in Options then + AbortCompileFmt(SCompilerParamErrorBadCombo2, [ParamCommonFlags, 'download', 'skipifsourcedoesntexist']) else if RecurseSubdirs then AbortCompileFmt(SCompilerParamErrorBadCombo2, [ParamCommonFlags, 'recursesubdirs', 'download']) else if ADestName = '' then diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index 449dd56a..b8b188ae 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -2146,6 +2146,8 @@ var SetProgress(ProgressBefore); ExpectedBytesLeft := CurFile^.ExternalSize; if foDownload in CurFile^.Options then begin + if foSkipIfSourceDoesntExist in CurFile^.Options then + InternalError('Unexpected SkipIfSourceDoesntExist flag'); if not(foCustomDestName in CurFile^.Options) then InternalError('Expected CustomDestName flag'); { CurFile^.DestName now includes a a filename, see TSetupCompiler.EnumFilesProc.ProcessFileList } From 5a830a1524f4b2ee759cd20c5cce02ed3cac5a53 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Mon, 9 Jun 2025 19:26:38 +0200 Subject: [PATCH 10/19] Fix leftover CallUpdate param which wasnt supposed to be there anymore. --- Projects/Src/Setup.Install.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index b8b188ae..4c7d99ae 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -78,7 +78,7 @@ begin WizardForm.FilenameLabel.Update; end; -procedure SetStatusLabelText(const S: String; const CallUpdate: Boolean = True; +procedure SetStatusLabelText(const S: String; const ClearFilenameLabelText: Boolean = True); begin if WizardForm.StatusLabel.Caption <> S then begin @@ -1254,7 +1254,7 @@ var raise; end; - { Update the filename label } + { Update the status and filename labels } if foDownload in CurFile^.Options then SetStatusLabelText(SetupMessages[msgStatusDownloadFiles], False) else From 125db1251c14213e837bb80df46ab2be1ed48411 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Mon, 9 Jun 2025 19:35:47 +0200 Subject: [PATCH 11/19] Do not try to include .issig size in the total progress. Instead just process events while downloading it without updating progress. New todo list: -Add DownloadRequiredSha256 parameter. (Or DownloadRequiredSHA256OfFile?) -Use another progressproc for the ArchiveFindExtract and 2 DownloadFile calls: it should prevent the progress bar position from moving beyond ExternalSize like CopySourceFileToDestFile does. -Update whatsnew. -Update CodeDownloadFiles references and add new ones. -Make a new fresh messages commit. -Add constant support to download username and extract/download passwords. (Extract on main.) -Test download username/password support. --- Components/ISSigFunc.pas | 1 - Projects/Src/Setup.Install.pas | 7 ++++++- Projects/Src/Setup.MainFunc.pas | 2 -- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Components/ISSigFunc.pas b/Components/ISSigFunc.pas index 04b2acb2..e80c1ae5 100644 --- a/Components/ISSigFunc.pas +++ b/Components/ISSigFunc.pas @@ -70,7 +70,6 @@ function ISSigCalcStreamHash(const AStream: TStream): TSHA256Digest; var ISSigExt: String = '.issig'; - ISSigEstimatedSize: Integer = 330; implementation diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index 4c7d99ae..d441fc08 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -226,6 +226,11 @@ begin ProcessEvents; end; +procedure JustProcessEventsProc(Bytes: Cardinal); +begin + ProcessEvents; +end; + function AbortRetryIgnoreTaskDialogMsgBox(const Text: String; const RetryIgnoreAbortButtonLabels: array of String): Boolean; { Returns True if Ignore was selected, False if Retry was selected, or @@ -1578,7 +1583,7 @@ var { Download the .issig file } const ISSigUrl = GetISSigUrl(SourceFile, CurFile^.DownloadISSigSource); DownloadFile(ISSigUrl, CurFile^.DownloadUserName, CurFile^.DownloadPassword, - ISSigDestF, False, '', '', ExtractorProgressProc); + ISSigDestF, False, '', '', JustProcessEventsProc); FreeAndNil(ISSigDestF); { Download and verify the actual file } DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword, diff --git a/Projects/Src/Setup.MainFunc.pas b/Projects/Src/Setup.MainFunc.pas index ea7a0122..4a369809 100644 --- a/Projects/Src/Setup.MainFunc.pas +++ b/Projects/Src/Setup.MainFunc.pas @@ -3550,8 +3550,6 @@ begin (ESevenZipError). Also see EnumFiles. } end; end; - if (foDownload in Options) and (foISSigVerify in Options) then - Inc6464(ExternalSize, Integer64(UInt64(ISSigEstimatedSize))); if Components = '' then { no types or a file that doesn't belong to any component } if (Tasks = '') and (Check = '') then {don't count tasks or scripted entries} Inc6464(MinimumSpace, ExternalSize); From 9471d2e64b306ac0a6fbfc77508cd5f010a8ffea Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Tue, 10 Jun 2025 20:12:32 +0200 Subject: [PATCH 12/19] Add constants support to DownloadISSigSource, DownloadUserName, and DownloadPassword + fix param parsing bug. --- ISHelp/isetup.xml | 6 +++--- Projects/Src/Compiler.SetupCompiler.pas | 11 +++++++---- Projects/Src/Setup.Install.pas | 10 ++++++---- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ISHelp/isetup.xml b/ISHelp/isetup.xml index aea51b52..01e2c22a 100644 --- a/ISHelp/isetup.xml +++ b/ISHelp/isetup.xml @@ -1671,18 +1671,18 @@ ExternalSize: 1_048_576; Flags: external -Specifies the URL of the .issig signature file which should be downloaded. This file is used to verify the file downloaded from the URL specified by the Source parameter.
+Specifies the URL of the .issig signature file which should be downloaded, which can include constants. This file is used to verify the file downloaded from the URL specified by the Source parameter.
This parameter is ignored if the download and issigverify flags aren't both also specified.
If this parameter is not set but both these flags are used, Setup will instead append ".issig" (without quotes) to the path portion of the URL specified by the Source parameter. It will then use the result as the URL to download the .issig signature file from.
-Specifies the basic authentication username to use for the file download.
+Specifies the basic authentication username to use for the file download, which can include constants.
This parameter is ignored if the download flag isn't also specified.
-Specifies the basic authentication password to use for the file download. Please be aware that this password is stored in an unencrypted form in the resulting Setup file(s), even if you have enabled encryption (using the [Setup] section directive Encryption).
+Specifies the basic authentication password to use for the file download, which can include constants. Please be aware that this password is stored in an unencrypted form in the resulting Setup file(s), even if you have enabled encryption (using the [Setup] section directive Encryption).
This parameter is ignored if the download flag isn't also specified.
diff --git a/Projects/Src/Compiler.SetupCompiler.pas b/Projects/Src/Compiler.SetupCompiler.pas index bf5a3a73..77092129 100644 --- a/Projects/Src/Compiler.SetupCompiler.pas +++ b/Projects/Src/Compiler.SetupCompiler.pas @@ -4668,9 +4668,9 @@ procedure TSetupCompiler.EnumFilesProc(const Line: PChar; const Ext: Integer); type TParam = (paFlags, paSource, paDestDir, paDestName, paCopyMode, paAttribs, paPermissions, paFontInstall, paExcludes, paExternalSize, paExtractArchivePassword, - paStrongAssemblyName, paISSigAllowedKeys, paComponents, paTasks, paLanguages, - paCheck, paBeforeInstall, paAfterInstall, paMinVersion, paOnlyBelowVersion, - paDownloadISSigSource, paDownloadUserName, paDownloadPassword); + paStrongAssemblyName, paISSigAllowedKeys, paDownloadISSigSource, paDownloadUserName, + paDownloadPassword, paComponents, paTasks, paLanguages, paCheck, paBeforeInstall, + paAfterInstall, paMinVersion, paOnlyBelowVersion); const ParamFilesSource = 'Source'; ParamFilesDestDir = 'DestDir'; @@ -5521,8 +5521,11 @@ begin CheckCheckOrInstall(ParamCommonCheck, Check, cikCheck); CheckCheckOrInstall(ParamCommonBeforeInstall, BeforeInstall, cikInstall); CheckCheckOrInstall(ParamCommonAfterInstall, AfterInstall, cikInstall); + CheckConst(DownloadISSigSource, MinVersion, []); + CheckConst(DownloadUserName, MinVersion, []); + CheckConst(DownloadPassword, MinVersion, []); CheckConst(ExtractArchivePassword, MinVersion, []); - end; + end; FileList := TList.Create(); DirList := TList.Create(); diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index 11e58f21..99e110d3 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -1576,17 +1576,19 @@ var { Download a file with or without ISSigVerify. Note: estimate of extra .issig size has already been added to CurFile's ExternalSize. } LastOperation := SetupMessages[msgErrorDownloading]; + const DownloadUserName = ExpandConst(CurFile^.DownloadUserName); + const DownloadPassword = ExpandConst(CurFile^.DownloadPassword); if foISSigVerify in CurFile^.Options then begin const ISSigTempFile = TempFile + ISSigExt; const ISSigDestF = TFileRedir.Create(DisableFsRedir, ISSigTempFile, fdCreateAlways, faReadWrite, fsNone); try { Download the .issig file } - const ISSigUrl = GetISSigUrl(SourceFile, CurFile^.DownloadISSigSource); - DownloadFile(ISSigUrl, CurFile^.DownloadUserName, CurFile^.DownloadPassword, + const ISSigUrl = GetISSigUrl(SourceFile, ExpandConst(CurFile^.DownloadISSigSource)); + DownloadFile(ISSigUrl, DownloadUserName, DownloadPassword, ISSigDestF, False, '', '', JustProcessEventsProc); FreeAndNil(ISSigDestF); { Download and verify the actual file } - DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword, + DownloadFile(SourceFile, DownloadUserName, DownloadPassword, DestF, True, CurFile^.ISSigAllowedKeys, TempFile, ExtractorProgressProc); finally ISSigDestF.Free; @@ -1594,7 +1596,7 @@ var DeleteFileRedir(DisableFsRedir, ISSigTempFile); end; end else - DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword, + DownloadFile(SourceFile, DownloadUserName, DownloadPassword, DestF, False, '', '', ExtractorProgressProc); end else begin From b953372c4783b4c8883be00a671a9710df0f39d9 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Wed, 11 Jun 2025 19:07:03 +0200 Subject: [PATCH 13/19] Temporary revert for clean new messages commit. --- Files/Default.isl | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/Files/Default.isl b/Files/Default.isl index 06b967d6..e40d51f6 100644 --- a/Files/Default.isl +++ b/Files/Default.isl @@ -1,4 +1,4 @@ -; *** Inno Setup version 6.5.0+ English messages *** +; *** Inno Setup version 6.4.0+ English messages *** ; ; To download user-contributed translations of this file, go to: ; https://jrsoftware.org/files/istrans/ @@ -224,18 +224,13 @@ ErrorFileHash2=Invalid file hash: expected %1, found %2 ErrorProgress=Invalid progress: %1 of %2 ErrorFileSize=Invalid file size: expected %1, found %2 -; *** TExtractionWizardPage wizard page and ExtractArchive +; *** TExtractionWizardPage wizard page and Extract7ZipArchive ExtractionLabel=Extracting additional files... ButtonStopExtraction=&Stop extraction StopExtraction=Are you sure you want to stop the extraction? ErrorExtractionAborted=Extraction aborted ErrorExtractionFailed=Extraction failed: %1 -; *** Archive extraction failure details -ArchiveIncorrectPassword=The password is incorrect -ArchiveIsCorrupted=The archive is corrupted -ArchiveUnsupportedFormat=The archive format is unsupported - ; *** "Preparing to Install" wizard page WizardPreparing=Preparing to Install PreparingDesc=Setup is preparing to install [name] on your computer. @@ -285,7 +280,6 @@ AbortRetryIgnoreCancel=Cancel installation StatusClosingApplications=Closing applications... StatusCreateDirs=Creating directories... StatusExtractFiles=Extracting files... -StatusDownloadFiles=Downloading files... StatusCreateIcons=Creating shortcuts... StatusCreateIniEntries=Creating INI entries... StatusCreateRegistryEntries=Creating registry entries... @@ -315,12 +309,6 @@ FileAbortRetryIgnoreSkipNotRecommended=&Skip this file (not recommended) FileAbortRetryIgnoreIgnoreNotRecommended=&Ignore the error and continue (not recommended) SourceIsCorrupted=The source file is corrupted SourceDoesntExist=The source file "%1" does not exist -SourceVerificationFailed=Verification of the source file failed: %1 -VerificationSignatureDoesntExist=The signature file "%1" does not exist -VerificationSignatureInvalid=The signature file "%1" is invalid -VerificationKeyNotFound=The signature file "%1" uses an unknown key -VerificationFileSizeIncorrect=The size of the file is incorrect -VerificationFileHashIncorrect=The hash of the file is incorrect ExistingFileReadOnly2=The existing file could not be replaced because it is marked read-only. ExistingFileReadOnlyRetry=&Remove the read-only attribute and try again ExistingFileReadOnlyKeepExisting=&Keep the existing file @@ -339,8 +327,6 @@ ErrorChangingAttr=An error occurred while trying to change the attributes of the ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory: ErrorReadingSource=An error occurred while trying to read the source file: ErrorCopying=An error occurred while trying to copy a file: -ErrorDownloading=An error occurred while trying to download a file: -ErrorExtracting=An error occurred while trying to extract an archive: ErrorReplacingExistingFile=An error occurred while trying to replace the existing file: ErrorRestartReplace=RestartReplace failed: ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory: From f832ee268b0798ee5528b27a97f6fac73c93bf0a Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Wed, 11 Jun 2025 19:09:05 +0200 Subject: [PATCH 14/19] New messages: -ArchiveIncorrectPassword, ArchiveIsCorrupted, ArchiveUnsupportedFormat -StatusDownloadFiles -SourceVerificationFailed, VerificationSignatureDoesntExist, VerificationSignatureInvalid, VerificationKeyNotFound, VerificationFileSizeIncorrect, VerificationFileHashIncorrect -ErrorDownloading, ErrorExtracting Also update a comment for Extract7ZipArchive rename. --- Files/Default.isl | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Files/Default.isl b/Files/Default.isl index e40d51f6..06b967d6 100644 --- a/Files/Default.isl +++ b/Files/Default.isl @@ -1,4 +1,4 @@ -; *** Inno Setup version 6.4.0+ English messages *** +; *** Inno Setup version 6.5.0+ English messages *** ; ; To download user-contributed translations of this file, go to: ; https://jrsoftware.org/files/istrans/ @@ -224,13 +224,18 @@ ErrorFileHash2=Invalid file hash: expected %1, found %2 ErrorProgress=Invalid progress: %1 of %2 ErrorFileSize=Invalid file size: expected %1, found %2 -; *** TExtractionWizardPage wizard page and Extract7ZipArchive +; *** TExtractionWizardPage wizard page and ExtractArchive ExtractionLabel=Extracting additional files... ButtonStopExtraction=&Stop extraction StopExtraction=Are you sure you want to stop the extraction? ErrorExtractionAborted=Extraction aborted ErrorExtractionFailed=Extraction failed: %1 +; *** Archive extraction failure details +ArchiveIncorrectPassword=The password is incorrect +ArchiveIsCorrupted=The archive is corrupted +ArchiveUnsupportedFormat=The archive format is unsupported + ; *** "Preparing to Install" wizard page WizardPreparing=Preparing to Install PreparingDesc=Setup is preparing to install [name] on your computer. @@ -280,6 +285,7 @@ AbortRetryIgnoreCancel=Cancel installation StatusClosingApplications=Closing applications... StatusCreateDirs=Creating directories... StatusExtractFiles=Extracting files... +StatusDownloadFiles=Downloading files... StatusCreateIcons=Creating shortcuts... StatusCreateIniEntries=Creating INI entries... StatusCreateRegistryEntries=Creating registry entries... @@ -309,6 +315,12 @@ FileAbortRetryIgnoreSkipNotRecommended=&Skip this file (not recommended) FileAbortRetryIgnoreIgnoreNotRecommended=&Ignore the error and continue (not recommended) SourceIsCorrupted=The source file is corrupted SourceDoesntExist=The source file "%1" does not exist +SourceVerificationFailed=Verification of the source file failed: %1 +VerificationSignatureDoesntExist=The signature file "%1" does not exist +VerificationSignatureInvalid=The signature file "%1" is invalid +VerificationKeyNotFound=The signature file "%1" uses an unknown key +VerificationFileSizeIncorrect=The size of the file is incorrect +VerificationFileHashIncorrect=The hash of the file is incorrect ExistingFileReadOnly2=The existing file could not be replaced because it is marked read-only. ExistingFileReadOnlyRetry=&Remove the read-only attribute and try again ExistingFileReadOnlyKeepExisting=&Keep the existing file @@ -327,6 +339,8 @@ ErrorChangingAttr=An error occurred while trying to change the attributes of the ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory: ErrorReadingSource=An error occurred while trying to read the source file: ErrorCopying=An error occurred while trying to copy a file: +ErrorDownloading=An error occurred while trying to download a file: +ErrorExtracting=An error occurred while trying to extract an archive: ErrorReplacingExistingFile=An error occurred while trying to replace the existing file: ErrorRestartReplace=RestartReplace failed: ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory: From 941d305bda51e99d12293f5a030e73057941a50a Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Wed, 11 Jun 2025 19:12:17 +0200 Subject: [PATCH 15/19] Update. --- whatsnew.htm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/whatsnew.htm b/whatsnew.htm index 2b9412ed..e96be450 100644 --- a/whatsnew.htm +++ b/whatsnew.htm @@ -183,13 +183,14 @@ issigtool --key-file="MyKey.ispublickey" verify "MyProg.dll"Some messages have been added and changed in this version: (View differences in Default.isl.)
+Some messages have been added and changed in this version: (View differences in Default.isl.)
Instructs the compiler or Setup to verify the source file's signature using a key from the [ISSigKeys] section, allowing all keys by default. Use the ISSigAllowedKeys parameter to limit the allowed keys.
-The verification requires an .issig signature file to be present in the same directory as the source file, created using the Inno Setup Signature Tool.
+The verification requires an .issig signature file to be present in the same directory as the source file, created using the Inno Setup Signature Tool. If flag download is set then the .issig signature file will be downloaded instead. See the DownloadISSigSource parameter description for more information..
The precise effect of this flag depends on whether it is combined with the external flag:
Support for downloading files has been improved: the [Files] section now supports file downloads. Writing Pascal Script to download a file is no longer necessary and is in fact less efficient since it requires an intermediate temporary file which this new download support doesn't.
+[Files] +Source: "https://jrsoftware.org/download.php/is.exe?dontcount=1"; DestName: "innosetup-latest.exe"; DestDir: "{app}"; \ + ExternalSize: 7_000_000; Flags: external download ignoreversion+
Inno Setup now includes an integrated signature-verification capability that can be used to detect corruption or tampering in your files at compile time, before files are included in an installer being built, or during installation, before Setup copies external files onto a user's system.
Any type of file may be signed and verified, and creation of signatures does not require a certificate from a certificate authority. There is no cost involved.
From 2a32a4527760618255bdf348bb5b88ef64a03684 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Wed, 11 Jun 2025 20:22:33 +0200 Subject: [PATCH 18/19] Use the same progress protection while extracting and downloading as while copying an external file. --- .../Src/Compression.SevenZipDLLDecoder.pas | 34 ++++---- Projects/Src/Setup.FileExtractor.pas | 4 +- Projects/Src/Setup.Install.pas | 86 ++++++++++--------- Projects/Src/Shared.Int64Em.pas | 9 +- 4 files changed, 69 insertions(+), 64 deletions(-) diff --git a/Projects/Src/Compression.SevenZipDLLDecoder.pas b/Projects/Src/Compression.SevenZipDLLDecoder.pas index aa2f26cc..1f0382d8 100644 --- a/Projects/Src/Compression.SevenZipDLLDecoder.pas +++ b/Projects/Src/Compression.SevenZipDLLDecoder.pas @@ -15,7 +15,7 @@ unit Compression.SevenZipDLLDecoder; interface uses - Windows, Shared.FileClass, Shared.VerInfoFunc, Compression.SevenZipDecoder; + Windows, Shared.FileClass, Shared.VerInfoFunc, Shared.Int64Em, Compression.SevenZipDecoder; function SevenZipDLLInit(const SevenZipLibrary: HMODULE; [ref] const VersionNumbers: TFileVersionNumbers): Boolean; @@ -33,7 +33,7 @@ procedure ExtractArchiveRedir(const DisableFsRedir: Boolean; was found. } type TArchiveFindHandle = type NativeUInt; - TOnExtractToHandleProgress = procedure(Bytes: Cardinal); + TOnExtractToHandleProgress = procedure(const Bytes, Param: Integer64); function ArchiveFindFirstFileRedir(const DisableFsRedir: Boolean; const ArchiveFilename, DestDir, Password: String; const RecurseSubDirs, ExtractIntent: Boolean; @@ -41,7 +41,7 @@ function ArchiveFindFirstFileRedir(const DisableFsRedir: Boolean; function ArchiveFindNextFile(const FindFile: TArchiveFindHandle; out FindFileData: TWin32FindData): Boolean; function ArchiveFindClose(const FindFile: TArchiveFindHandle): Boolean; procedure ArchiveFindExtract(const FindFile: TArchiveFindHandle; const DestF: TFile; - const OnExtractToHandleProgress: TOnExtractToHandleProgress); + const OnExtractToHandleProgress: TOnExtractToHandleProgress; const OnExtractToHandleProgressParam: Integer64); type TFileTimeHelper = record helper for TFileTime @@ -54,7 +54,7 @@ implementation uses Classes, SysUtils, Forms, Variants, ActiveX, ComObj, Generics.Collections, Compression.SevenZipDLLDecoder.Interfaces, PathFunc, - Shared.Int64Em, Shared.SetupMessageIDs, Shared.CommonFunc, + Shared.SetupMessageIDs, Shared.CommonFunc, SetupLdrAndSetup.Messages, SetupLdrAndSetup.RedirFunc, Setup.LoggingFunc, Setup.MainFunc, Setup.InstFunc; @@ -175,6 +175,7 @@ type FIndex: UInt32; FDestF: TFile; FOnExtractToHandleProgress: TOnExtractToHandleProgress; + FOnExtractToHandleProgressParam: Integer64; FPreviousProgress: UInt64; protected { IArchiveExtractCallback } @@ -186,7 +187,8 @@ type public constructor Create(const InArchive: IInArchive; const numItems: UInt32; const Password: String; const Index: UInt32; const DestF: TFile; - const OnExtractToHandleProgress: TOnExtractToHandleProgress); + const OnExtractToHandleProgress: TOnExtractToHandleProgress; + const OnExtractToHandleProgressParam: Integer64); end; { Helper functions } @@ -779,12 +781,14 @@ end; constructor TArchiveExtractToHandleCallback.Create(const InArchive: IInArchive; const numItems: UInt32; const Password: String; const Index: UInt32; - const DestF: TFile; const OnExtractToHandleProgress: TOnExtractToHandleProgress); + const DestF: TFile; const OnExtractToHandleProgress: TOnExtractToHandleProgress; + const OnExtractToHandleProgressParam: Integer64); begin inherited Create(InArchive, numItems, Password); FIndex := Index; FDestF := DestF; FOnExtractToHandleProgress := OnExtractToHandleProgress; + FOnExtractToHandleProgressParam := OnExtractToHandleProgressParam; end; function TArchiveExtractToHandleCallback.GetIndices: TArchiveExtractBaseCallback.TArrayOfUInt32; @@ -837,17 +841,7 @@ begin System.TMonitor.Exit(FLock); end; - { Also see Setup.Install THTTPDataReceiver.OnReceiveData } - var Bytes := Progress - FPreviousProgress; - while Bytes > 0 do begin - var BytesToReport: Cardinal; - if Bytes > High(BytesToReport) then - BytesToReport := High(BytesToReport) - else - BytesToReport := Bytes; - FOnExtractToHandleProgress(BytesToReport); - Dec(Bytes, BytesToReport); - end; + FOnExtractToHandleProgress(Integer64(Progress-FPreviousProgress), FOnExtractToHandleProgressParam); FPreviousProgress := Progress; end; end; @@ -1124,7 +1118,8 @@ begin end; procedure ArchiveFindExtract(const FindFile: TArchiveFindHandle; const DestF: TFile; - const OnExtractToHandleProgress: TOnExtractToHandleProgress); + const OnExtractToHandleProgress: TOnExtractToHandleProgress; + const OnExtractToHandleProgressParam: Integer64); begin const State = ArchiveFindStates[CheckFindFileHandle(FindFile)]; @@ -1135,7 +1130,8 @@ begin const ExtractCallback: IArchiveExtractCallback = TArchiveExtractToHandleCallback.Create(State.InArchive, State.numItems, - State.Password, State.currentIndex, DestF, OnExtractToHandleProgress); + State.Password, State.currentIndex, DestF, OnExtractToHandleProgress, + OnExtractToHandleProgressParam); (ExtractCallback as TArchiveExtractToHandleCallback).Extract; end; diff --git a/Projects/Src/Setup.FileExtractor.pas b/Projects/Src/Setup.FileExtractor.pas index 720e4660..db5b1159 100644 --- a/Projects/Src/Setup.FileExtractor.pas +++ b/Projects/Src/Setup.FileExtractor.pas @@ -2,7 +2,7 @@ unit Setup.FileExtractor; { Inno Setup - Copyright (C) 1997-2010 Jordan Russell + Copyright (C) 1997-2025 Jordan Russell Portions by Martijn Laan For conditions of distribution and use, see LICENSE.TXT. @@ -16,7 +16,7 @@ uses Shared.Struct, ChaCha20; type - TExtractorProgressProc = procedure(Bytes: Cardinal); + TExtractorProgressProc = procedure(const Bytes: Cardinal); TFileExtractor = class private diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index 99e110d3..f19c617b 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -12,7 +12,7 @@ unit Setup.Install; interface uses - Classes, SHA256, Shared.FileClass, Shared.SetupTypes; + Classes, SHA256, Shared.FileClass, Shared.SetupTypes, Shared.Int64Em; procedure ISSigVerifyError(const AError: TISSigVerifySignatureError; const ASigFilename: String = ''); @@ -26,14 +26,15 @@ procedure PerformInstall(var Succeeded: Boolean; const ChangesEnvironment, type TOnDownloadProgress = function(const Url, BaseName: string; const Progress, ProgressMax: Int64): Boolean of object; - TOnSimpleDownloadProgress = procedure(Bytes: Cardinal); + TOnSimpleDownloadProgress = procedure(const Bytes, Param: Integer64); procedure ExtractTemporaryFile(const BaseName: String); function ExtractTemporaryFiles(const Pattern: String): Integer; function DownloadFile(const Url, CustomUserName, CustomPassword: String; const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; const ISSigSourceFilename: String; - const OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64; + const OnSimpleDownloadProgress: TOnSimpleDownloadProgress; + const OnSimpleDownloadProgressParam: Integer64): Int64; function DownloadTemporaryFile(const Url, BaseName, RequiredSHA256OfFile: String; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; const OnDownloadProgress: TOnDownloadProgress): Int64; @@ -49,7 +50,7 @@ uses SetupLdrAndSetup.InstFunc, Setup.InstFunc, Setup.InstFunc.Ole, Setup.SecurityFunc, SetupLdrAndSetup.Messages, Setup.MainFunc, Setup.LoggingFunc, Setup.FileExtractor, Compression.Base, PathFunc, ISSigFunc, Shared.CommonFunc.Vcl, Compression.SevenZipDLLDecoder, - Shared.CommonFunc, SetupLdrAndSetup.RedirFunc, Shared.Int64Em, Shared.SetupMessageIDs, + Shared.CommonFunc, SetupLdrAndSetup.RedirFunc, Shared.SetupMessageIDs, Setup.WizardForm, Shared.DebugStruct, Setup.DebugClient, Shared.VerInfoFunc, Setup.ScriptRunner, Setup.RegDLL, Setup.Helper, Shared.ResUpdateFunc, Setup.DotNetFunc, TaskbarProgressFunc, NewProgressBar, RestartManager, Net.HTTPClient, Net.URLClient, NetEncoding, RegStr; @@ -220,13 +221,26 @@ begin if NeedToAbortInstall then Abort; end; -procedure ExtractorProgressProc(Bytes: Cardinal); +procedure InternalProgressProc(const Bytes: Cardinal); begin IncProgress(Bytes); ProcessEvents; end; -procedure JustProcessEventsProc(Bytes: Cardinal); +procedure ExternalProgressProc64(const Bytes, MaxProgress: Integer64); +begin + var NewProgress := CurProgress; + Inc6464(NewProgress, Bytes); + { In case the source file was larger than we thought it was, stop the + progress bar at the maximum amount. Also see CopySourceFileToDestFile. } + if Compare64(NewProgress, MaxProgress) > 0 then + NewProgress := MaxProgress; + SetProgress(NewProgress); + + ProcessEvents; +end; + +procedure JustProcessEventsProc64(const Bytes, Param: Integer64); begin ProcessEvents; end; @@ -341,7 +355,6 @@ procedure CopySourceFileToDestFile(const SourceF, DestF: TFile; goes. Assumes file pointers of both are 0. } var BytesLeft: Integer64; - NewProgress: Integer64; BufSize: Cardinal; Buf: array[0..16383] of Byte; Context: TSHA256Context; @@ -377,15 +390,7 @@ begin if ISSigVerify then SHA256Update(Context, Buf, BufSize); - NewProgress := CurProgress; - Inc64(NewProgress, BufSize); - { In case the source file was larger than we thought it was, stop the - progress bar at the maximum amount } - if Compare64(NewProgress, MaxProgress) > 0 then - NewProgress := MaxProgress; - SetProgress(NewProgress); - - ProcessEvents; + ExternalProgressProc64(To64(BufSize), MaxProgress); end; if ISSigVerify then begin @@ -1561,16 +1566,18 @@ var LastOperation := SetupMessages[msgErrorReadingSource]; if SourceFile = '' then begin { Decompress a file } - FileExtractor.SeekTo(CurFileLocation^, ExtractorProgressProc); + FileExtractor.SeekTo(CurFileLocation^, InternalProgressProc); LastOperation := SetupMessages[msgErrorCopying]; - FileExtractor.DecompressFile(CurFileLocation^, DestF, ExtractorProgressProc, + FileExtractor.DecompressFile(CurFileLocation^, DestF, InternalProgressProc, not (foDontVerifyChecksum in CurFile^.Options)); end else if foExtractArchive in CurFile^.Options then begin { Extract a file from archive. Note: ISSigVerify for archive has already been handled by RecurseExternalArchiveCopyFiles. } LastOperation := SetupMessages[msgErrorExtracting]; - ArchiveFindExtract(StrToInt(SourceFile), DestF, ExtractorProgressProc); + var MaxProgress := CurProgress; + Inc6464(MaxProgress, AExternalSize); + ArchiveFindExtract(StrToInt(SourceFile), DestF, ExternalProgressProc64, MaxProgress); end else if foDownload in CurFile^.Options then begin { Download a file with or without ISSigVerify. Note: estimate of @@ -1578,6 +1585,8 @@ var LastOperation := SetupMessages[msgErrorDownloading]; const DownloadUserName = ExpandConst(CurFile^.DownloadUserName); const DownloadPassword = ExpandConst(CurFile^.DownloadPassword); + var MaxProgress := CurProgress; + Inc6464(MaxProgress, AExternalSize); if foISSigVerify in CurFile^.Options then begin const ISSigTempFile = TempFile + ISSigExt; const ISSigDestF = TFileRedir.Create(DisableFsRedir, ISSigTempFile, fdCreateAlways, faReadWrite, fsNone); @@ -1585,11 +1594,11 @@ var { Download the .issig file } const ISSigUrl = GetISSigUrl(SourceFile, ExpandConst(CurFile^.DownloadISSigSource)); DownloadFile(ISSigUrl, DownloadUserName, DownloadPassword, - ISSigDestF, False, '', '', JustProcessEventsProc); + ISSigDestF, False, '', '', JustProcessEventsProc64, To64(0)); FreeAndNil(ISSigDestF); { Download and verify the actual file } DownloadFile(SourceFile, DownloadUserName, DownloadPassword, - DestF, True, CurFile^.ISSigAllowedKeys, TempFile, ExtractorProgressProc); + DestF, True, CurFile^.ISSigAllowedKeys, TempFile, ExternalProgressProc64, MaxProgress); finally ISSigDestF.Free; { Delete the .issig file } @@ -1597,7 +1606,7 @@ var end; end else DownloadFile(SourceFile, DownloadUserName, DownloadPassword, - DestF, False, '', '', ExtractorProgressProc); + DestF, False, '', '', ExternalProgressProc64, MaxProgress); end else begin { Copy a duplicated non-external file, or an external file } @@ -3676,6 +3685,7 @@ type FBaseName, FUrl: String; FOnDownloadProgress: TOnDownloadProgress; FOnSimpleDownloadProgress: TOnSimpleDownloadProgress; + FOnSimpleDownloadProgressParam: Integer64; FAborted: Boolean; FProgress, FProgressMax: Int64; FLastReportedProgress, FLastReportedProgressMax: Int64; @@ -3684,6 +3694,7 @@ type property Url: String write FUrl; property OnDownloadProgress: TOnDownloadProgress write FOnDownloadProgress; property OnSimpleDownloadProgress: TOnSimpleDownloadProgress write FOnSimpleDownloadProgress; + property OnSimpleDownloadProgressParam: Integer64 write FOnSimpleDownloadProgressParam; property Aborted: Boolean read FAborted; property Progress: Int64 read FProgress; property ProgressMax: Int64 read FProgressMax; @@ -3719,25 +3730,14 @@ begin if Abort then FAborted := True end else if Assigned(FOnSimpleDownloadProgress) then begin - { Also see Compression.SevenZipDLLDecoder TArchiveExtractToHandleCallback.HandleProgress } - var Bytes := Progress - FLastReportedProgress; - while Bytes > 0 do begin - var BytesToReport: Cardinal; - if Bytes > High(BytesToReport) then - BytesToReport := High(BytesToReport) - else - BytesToReport := Bytes; - try - FOnSimpleDownloadProgress(BytesToReport); - except - if ExceptObject is EAbort then begin - Abort := True; - FAborted := True; - Break; - end else - raise; - end; - Dec(Bytes, BytesToReport); + try + FOnSimpleDownloadProgress(Integer64(Progress-FLastReportedProgress), FOnSimpleDownloadProgressParam); + except + if ExceptObject is EAbort then begin + Abort := True; + FAborted := True; + end else + raise; end; FLastReportedProgress := Progress; end; @@ -3806,7 +3806,8 @@ end; function DownloadFile(const Url, CustomUserName, CustomPassword: String; const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; const ISSigSourceFilename: String; - const OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64; + const OnSimpleDownloadProgress: TOnSimpleDownloadProgress; + const OnSimpleDownloadProgressParam: Integer64): Int64; var HandleStream: THandleStream; HTTPDataReceiver: THTTPDataReceiver; @@ -3832,6 +3833,7 @@ begin HTTPDataReceiver := THTTPDataReceiver.Create; HTTPDataReceiver.Url := CleanUrl; HTTPDataReceiver.OnSimpleDownloadProgress := OnSimpleDownloadProgress; + HTTPDataReceiver.OnSimpleDownloadProgressParam := OnSimpleDownloadProgressParam; HTTPClient := THTTPClient.Create; { http://docwiki.embarcadero.com/RADStudio/Rio/en/Using_an_HTTP_Client } SetUserAgentAndSecureProtocols(HTTPClient); diff --git a/Projects/Src/Shared.Int64Em.pas b/Projects/Src/Shared.Int64Em.pas index 778e1cee..68dbc986 100644 --- a/Projects/Src/Shared.Int64Em.pas +++ b/Projects/Src/Shared.Int64Em.pas @@ -2,7 +2,7 @@ unit Shared.Int64Em; { Inno Setup - Copyright (C) 1997-2024 Jordan Russell + Copyright (C) 1997-2025 Jordan Russell Portions by Martijn Laan For conditions of distribution and use, see LICENSE.TXT. @@ -31,6 +31,7 @@ function Mul64(var X: Integer64; N: LongWord): Boolean; procedure Multiply32x32to64(N1, N2: LongWord; var X: Integer64); procedure Shr64(var X: Integer64; Count: LongWord); function StrToInteger64(const S: String; var X: Integer64): Boolean; +function To64(const Lo: Longword): Integer64; implementation @@ -287,4 +288,10 @@ begin SetString(Result, PChar(@Buf[I]), (High(Buf) + 1) - I); end; +function To64(const Lo: Longword): Integer64; +begin + Result.Lo := Lo; + Result.Hi := 0; +end; + end. From 487b78d6dc1a45a59f569d78ab7dd7d1a227b3c1 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Wed, 11 Jun 2025 20:46:41 +0200 Subject: [PATCH 19/19] Cleanup. --- Projects/Src/Compiler.CompressionHandler.pas | 6 ++---- Projects/Src/Compiler.SetupCompiler.pas | 3 +-- Projects/Src/Setup.FileExtractor.pas | 3 +-- Projects/Src/Setup.Install.pas | 11 +++-------- Projects/Src/Setup.MainFunc.pas | 9 +++------ Projects/Src/Setup.ScriptFunc.pas | 2 +- Projects/Src/Setup.UninstallLog.pas | 5 ++--- Projects/Src/Setup.WizardForm.pas | 6 ++---- Projects/Src/Shared.FileClass.pas | 8 ++------ Projects/Src/Shared.Int64Em.pas | 3 +-- 10 files changed, 18 insertions(+), 38 deletions(-) diff --git a/Projects/Src/Compiler.CompressionHandler.pas b/Projects/Src/Compiler.CompressionHandler.pas index bd70cf72..8f23a690 100644 --- a/Projects/Src/Compiler.CompressionHandler.pas +++ b/Projects/Src/Compiler.CompressionHandler.pas @@ -215,10 +215,8 @@ begin FChunkStartOffset := FDestFile.Position.Lo - FSliceBaseOffset; FDestFile.WriteBuffer(ZLIBID, SizeOf(ZLIBID)); Dec(FSliceBytesLeft, SizeOf(ZLIBID)); - FChunkBytesRead.Hi := 0; - FChunkBytesRead.Lo := 0; - FChunkBytesWritten.Hi := 0; - FChunkBytesWritten.Lo := 0; + FChunkBytesRead := To64(0); + FChunkBytesWritten := To64(0); FInitialBytesCompressedSoFar := FCompiler.GetBytesCompressedSoFar; SelectCompressor; diff --git a/Projects/Src/Compiler.SetupCompiler.pas b/Projects/Src/Compiler.SetupCompiler.pas index 77092129..d9e36f7d 100644 --- a/Projects/Src/Compiler.SetupCompiler.pas +++ b/Projects/Src/Compiler.SetupCompiler.pas @@ -5211,8 +5211,7 @@ begin NoCompression := False; NoEncryption := False; SolidBreak := False; - ExternalSize.Hi := 0; - ExternalSize.Lo := 0; + ExternalSize := To64(0); SortFilesByName := False; Sign := fsNoSetting; diff --git a/Projects/Src/Setup.FileExtractor.pas b/Projects/Src/Setup.FileExtractor.pas index db5b1159..743bb3be 100644 --- a/Projects/Src/Setup.FileExtractor.pas +++ b/Projects/Src/Setup.FileExtractor.pas @@ -256,8 +256,7 @@ begin FChunkLastSlice := FL.LastSlice; FChunkStartOffset := FL.StartOffset; FChunkBytesLeft := FL.ChunkCompressedSize; - FChunkDecompressedBytesRead.Hi := 0; - FChunkDecompressedBytesRead.Lo := 0; + FChunkDecompressedBytesRead := To64(0); FChunkCompressed := floChunkCompressed in FL.Flags; FChunkEncrypted := floChunkEncrypted in FL.Flags; diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index f19c617b..0c8fdced 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -118,8 +118,7 @@ var CurFile: PSetupFileEntry; FileSize: Integer64; begin - InstallFilesSize.Hi := 0; - InstallFilesSize.Lo := 0; + InstallFilesSize := To64(0); AfterInstallFilesSize := InstallFilesSize; for N := 0 to Entries[seFile].Count-1 do begin CurFile := PSetupFileEntry(Entries[seFile][N]); @@ -143,8 +142,7 @@ var NewMaxValue: Integer64; begin { Calculate the MaxValue for the progress meter } - NewMaxValue.Hi := 0; - NewMaxValue.Lo := 1000 * Entries[seIcon].Count; + NewMaxValue := To64(1000 * Entries[seIcon].Count); if Entries[seIni].Count <> 0 then Inc(NewMaxValue.Lo, 1000); if Entries[seRegistry].Count <> 0 then Inc(NewMaxValue.Lo, 1000); Inc6464(NewMaxValue, InstallFilesSize); @@ -2104,7 +2102,6 @@ var I: Integer; CurFileNumber: Integer; CurFile: PSetupFileEntry; - ExternalSize: Integer64; SourceWildcard: String; ProgressBefore, ExpectedBytesLeft: Integer64; DisableFsRedir, FoundFiles: Boolean; @@ -2143,9 +2140,7 @@ var end; if CurFile^.LocationEntry <> -1 then begin - ExternalSize.Hi := 0; { not used... } - ExternalSize.Lo := 0; - ProcessFileEntry(CurFile, DisableFsRedir, '', '', FileLocationFilenames, ExternalSize, + ProcessFileEntry(CurFile, DisableFsRedir, '', '', FileLocationFilenames, To64(0), ConfirmOverwriteOverwriteAll, PromptIfOlderOverwriteAll, WarnedPerUserFonts, nil); end else begin diff --git a/Projects/Src/Setup.MainFunc.pas b/Projects/Src/Setup.MainFunc.pas index 1d2ea52e..89b802ce 100644 --- a/Projects/Src/Setup.MainFunc.pas +++ b/Projects/Src/Setup.MainFunc.pas @@ -1696,8 +1696,7 @@ var ComponentTypes: TStringList; I: Integer; begin - Result.Hi := 0; - Result.Lo := 0; + Result := To64(0); ComponentTypes := TStringList.Create(); for I := 0 to Entries[seComponent].Count-1 do begin @@ -2780,8 +2779,7 @@ var begin { Also see RecurseExternalFiles above and RecurseExternalCopyFiles in Setup.Install Also see RecurseExternalArchiveGetSizeOfFiles directly below } - Result.Hi := 0; - Result.Lo := 0; + Result := To64(0); var FindData: TWin32FindData; var H := FindFirstFileRedir(DisableFsRedir, SearchBaseDir + SearchSubDir + SearchWildcard, FindData); @@ -2829,8 +2827,7 @@ var const RecurseSubDirs: Boolean): Integer64; begin { See above } - Result.Hi := 0; - Result.Lo := 0; + Result := To64(0); var FindData: TWin32FindData; var H := ArchiveFindFirstFileRedir(DisableFsRedir, ArchiveFilename, diff --git a/Projects/Src/Setup.ScriptFunc.pas b/Projects/Src/Setup.ScriptFunc.pas index 62123dae..8df835b5 100644 --- a/Projects/Src/Setup.ScriptFunc.pas +++ b/Projects/Src/Setup.ScriptFunc.pas @@ -921,7 +921,7 @@ var Div64(FreeBytes, 1024*1024); Div64(TotalBytes, 1024*1024); end; - { Cap at 2 GB, as [Code] doesn't support 64-bit integers } + { Cap at 2 GB, as GetSpaceOnDisk doesn't use 64-bit integers } if (FreeBytes.Hi <> 0) or (FreeBytes.Lo and $80000000 <> 0) then FreeBytes.Lo := $7FFFFFFF; if (TotalBytes.Hi <> 0) or (TotalBytes.Lo and $80000000 <> 0) then diff --git a/Projects/Src/Setup.UninstallLog.pas b/Projects/Src/Setup.UninstallLog.pas index 1b112910..095a2408 100644 --- a/Projects/Src/Setup.UninstallLog.pas +++ b/Projects/Src/Setup.UninstallLog.pas @@ -2,7 +2,7 @@ unit Setup.UninstallLog; { Inno Setup - Copyright (C) 1997-2024 Jordan Russell + Copyright (C) 1997-2025 Jordan Russell Portions by Martijn Laan For conditions of distribution and use, see LICENSE.TXT. @@ -1292,8 +1292,7 @@ var EndOffset, Ofs: Integer64; CrcHeader: TUninstallCrcHeader; begin - EndOffset.Lo := Header.EndOffset; - EndOffset.Hi := 0; + EndOffset := To64(Header.EndOffset); while BufLeft = 0 do begin Ofs := F.Position; Inc64(Ofs, SizeOf(CrcHeader)); diff --git a/Projects/Src/Setup.WizardForm.pas b/Projects/Src/Setup.WizardForm.pas index df7e3942..53506db6 100644 --- a/Projects/Src/Setup.WizardForm.pas +++ b/Projects/Src/Setup.WizardForm.pas @@ -614,8 +614,7 @@ var begin ComponentEntry := PSetupComponentEntry(ComponentsList.ItemObject[Index]); - ChildrenSize.Hi := 0; - ChildrenSize.Lo := 0; + ChildrenSize := To64(0); if HasChildren then ComponentsList.EnumChildrenOf(Index, UpdateComponentSizesEnum, LongInt(@ChildrenSize)); ComponentSize := ComponentEntry.Size; @@ -637,8 +636,7 @@ var Size: Integer64; begin if shShowComponentSizes in SetupHeader.Options then begin - Size.Hi := 0; - Size.Lo := 0; + Size := To64(0); ComponentsList.EnumChildrenOf(-1, UpdateComponentSizesEnum, LongInt(@Size)); end; end; diff --git a/Projects/Src/Shared.FileClass.pas b/Projects/Src/Shared.FileClass.pas index df6c514f..3a526e2c 100644 --- a/Projects/Src/Shared.FileClass.pas +++ b/Projects/Src/Shared.FileClass.pas @@ -2,7 +2,7 @@ unit Shared.FileClass; { Inno Setup - Copyright (C) 1997-2024 Jordan Russell + Copyright (C) 1997-2025 Jordan Russell Portions by Martijn Laan For conditions of distribution and use, see LICENSE.TXT. @@ -197,12 +197,8 @@ begin end; procedure TCustomFile.Seek(Offset: Cardinal); -var - I: Integer64; begin - I.Hi := 0; - I.Lo := Offset; - Seek64(I); + Seek64(To64(Offset)); end; procedure TCustomFile.WriteAnsiString(const S: AnsiString); diff --git a/Projects/Src/Shared.Int64Em.pas b/Projects/Src/Shared.Int64Em.pas index 68dbc986..18fa2d54 100644 --- a/Projects/Src/Shared.Int64Em.pas +++ b/Projects/Src/Shared.Int64Em.pas @@ -244,8 +244,7 @@ begin if (StartIndex > Len) or (S[StartIndex] = '_') then Exit; - V.Lo := 0; - V.Hi := 0; + V := To64(0); for I := StartIndex to Len do begin C := UpCase(S[I]); case C of