Merge branch 'files-download'

Going to deal with CodeDownloadFiles.iss and CodeDownloadFiles2.iss and all references later.

Also doing the Hash idea (before mentioned as DownloadRequiredSha256) later and not just for downloads.
This commit is contained in:
Martijn Laan 2025-06-11 21:10:13 +02:00
commit ecbc01910b
No known key found for this signature in database
GPG Key ID: E2DD568CF6098F6A
22 changed files with 494 additions and 139 deletions

View File

@ -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: 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}\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);

View File

@ -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;

View File

@ -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:

View File

@ -1555,7 +1555,8 @@ Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
<param name="Source" required="yes">
<p>The name of the <i>source file</i>. The compiler will prepend the path of your installation's <link topic="sourcedirectorynotes">source directory</link> if you do not specify a fully qualified pathname.</p>
<p>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.</p>
<p>When the flag <tt>external</tt> is specified, <tt>Source</tt> 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").</p>
<p>When the flag <tt>external</tt> is specified but the flag <tt>download</tt> is not, <tt>Source</tt> 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").</p>
<p>When the flag <tt>external</tt> is specified and the flag <tt>download</tt> is also, <tt>Source</tt> must be the URL of the file to download.</p>
<p>Constants may only be used when the <tt>external</tt> flag is specified, because the compiler does not do any constant translating itself.</p>
<examples>
<pre>
@ -1669,6 +1670,22 @@ ExternalSize: 1_048_576; Flags: external
<p>This parameter is ignored if the <tt>extractarchive</tt> flag isn't also specified.</p>
</param>
<param name="DownloadISSigSource">
<p>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 <tt>Source</tt> parameter.</p>
<p>This parameter is ignored if the <tt>download</tt> and <tt>issigverify</tt> flags aren't both also specified.</p>
<p>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 <tt>Source</tt> parameter. It will then use the result as the URL to download the .issig signature file from.</p>
</param>
<param name="DownloadUserName">
<p>Specifies the basic authentication username to use for the file download, which can include constants.</p>
<p>This parameter is ignored if the <tt>download</tt> flag isn't also specified.</p>
</param>
<param name="DownloadPassword">
<p>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 <tt>Encryption</tt>).</p>
<p>This parameter is ignored if the <tt>download</tt> flag isn't also specified.</p>
</param>
<param name="Flags">
<p>This parameter is a set of extra options. Multiple options may be used by separating them by spaces. The following options are supported:</p>
@ -1712,14 +1729,23 @@ Instructs Setup to proceed to comparing time stamps (last write/modified time) i
<p>Prevents Setup from verifying the file checksum after extraction. Use this flag on files you wish to modify while already compiled into Setup.</p>
<p>Must be combined with <tt>nocompression</tt>.</p>
</flag>
<flag name="download">
<p>This flag instructs Setup not to copy an existing file, but instead to download it. Optionally use the <tt>DownloadUserName</tt> and <tt>DownloadPassword</tt> parameters to specify a basic authentication username and password.</p>
<p>This flag must be combined with the <tt>DestName</tt> and <tt>ExternalSize</tt> parameters.</p>
<p>This flag also must be combined with the <tt>external</tt> and <tt>ignoreversion</tt> flags, meaning it should only be used on files private to your application, <i>never</i> on shared system files.</p>
<p>This flag cannot be combined with the <tt>comparetimestamp</tt>, <tt>extractarchive</tt>, <tt>recursesubdirs</tt>, and <tt>skipifsourcedoesntexist</tt> flags.</p>
<p>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.</p>
</flag>
<flag name="external">
<p>This flag instructs Inno Setup not to statically compile the file specified by the <tt>Source</tt> parameter into the installation files, but instead to copy from an existing file on the distribution media or the user's system. See the <tt>Source</tt> parameter description for more information.</p>
<p>When combined with the <tt>download</tt> or <tt>extractarchive</tt> flags, Setup does not copy the file, but instead downloads or extracts it.</p>
</flag>
<flag name="extractarchive">
<p>This flag instructs Inno Setup not to copy an existing archive file, but instead to extract it. Optionally use the <tt>ExtractArchivePassword</tt> parameter to specify a password.</p>
<p>This flag instructs Setup not to copy an existing archive file, but instead to extract it. Optionally use the <tt>ExtractArchivePassword</tt> parameter to specify a password.</p>
<p>The supported archive formats, beyond .7z, and the support for password-protected archives, depend on the <link topic="setup_archiveextraction">ArchiveExtraction</link> [Setup] section directive, that must not be set to <tt>basic</tt>.</p>
<p>This flag must be combined with the <tt>external</tt> and <tt>ignoreversion</tt> flags, meaning it should only be used on files private to your application, <i>never</i> on shared system files.</p>
<p>This flag is usually combined with the <tt>recursesubdirs</tt> and <tt>createallsubdirs</tt> flags.</p>
<p>This flag cannot be combined with <tt>download</tt>.</p>
<p>Using a solid archive is not recommended; extraction performance may degrade depending on the solid block size.</p>
</flag>
<flag name="fontisnttruetype">
@ -1737,7 +1763,7 @@ Instructs Setup to proceed to comparing time stamps (last write/modified time) i
</flag>
<flag name="issigverify">
<p>Instructs the compiler or Setup to verify the source file's signature using a key from the <link topic="issigkeyssection">[ISSigKeys] section</link>, allowing all keys by default. Use the <tt>ISSigAllowedKeys</tt> parameter to limit the allowed keys.</p>
<p>The verification requires an <tt>.issig</tt> signature file to be present in the same directory as the source file, created using the <link topic="issigtool">Inno Setup Signature Tool</link>.</p>
<p>The verification requires an <tt>.issig</tt> signature file to be present in the same directory as the source file, created using the <link topic="issigtool">Inno Setup Signature Tool</link>. If flag <tt>download</tt> is set then the <tt>.issig</tt> signature file will be downloaded instead. See the <tt>DownloadISSigSource</tt> parameter description for more information..</p>
<p>The precise effect of this flag depends on whether it is combined with the <tt>external</tt> flag:</p>
<ul>
<p>When used without the <tt>external</tt> flag, the compiler will verify the source file while it is being compressed/stored into the resulting installer. If the verification fails, compilation will abort.</p>

View File

@ -1862,7 +1862,7 @@ end;</pre>
<name>DownloadTemporaryFileWithISSigVerify</name>
<prototype>function DownloadTemporaryFileWithISSigVerify(const Url, ISSigUrl, BaseName: String; const AllowedKeysRuntimeIDs: TStringList; const OnDownloadProgress: TOnDownloadProgress): Int64;</prototype>
<description><p>Like <link topic="isxfunc_DownloadTemporaryFile">DownloadTemporaryFile</link>, 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.</p>
<p>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.</p>
<p>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.</p>
<p>Verification uses the specified allowed keys, looked up using <link topic="issigkeyssection">[ISSigKeys] section</link> parameter <tt>RuntimeID</tt>. To allow all keys set AllowedKeysRuntimeIDs to <tt>nil</tt>.</p>
<p>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.</p></description>
<seealso><p><link topic="isxfunc_DownloadTemporaryFile">DownloadTemporaryFile</link><br/>

View File

@ -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;

View File

@ -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';

View File

@ -4668,8 +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);
paStrongAssemblyName, paISSigAllowedKeys, paDownloadISSigSource, paDownloadUserName,
paDownloadPassword, paComponents, paTasks, paLanguages, paCheck, paBeforeInstall,
paAfterInstall, paMinVersion, paOnlyBelowVersion);
const
ParamFilesSource = 'Source';
ParamFilesDestDir = 'DestDir';
@ -4683,6 +4684,9 @@ const
ParamFilesExtractArchivePassword = 'ExtractArchivePassword';
ParamFilesStrongAssemblyName = 'StrongAssemblyName';
ParamFilesISSigAllowedKeys = 'ISSigAllowedKeys';
ParamFilesDownloadISSigSource = 'DownloadISSigSource';
ParamFilesDownloadUserName = 'DownloadUserName';
ParamFilesDownloadPassword = 'DownloadPassword';
ParamInfo: array[TParam] of TParamInfo = (
(Name: ParamCommonFlags; Flags: []),
(Name: ParamFilesSource; Flags: [piRequired, piNoEmpty, piNoQuotes]),
@ -4697,6 +4701,9 @@ 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: []),
(Name: ParamCommonTasks; Flags: []),
(Name: ParamCommonLanguages; Flags: []),
@ -4705,7 +4712,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 +4724,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 = (
@ -5204,8 +5211,7 @@ begin
NoCompression := False;
NoEncryption := False;
SolidBreak := False;
ExternalSize.Hi := 0;
ExternalSize.Lo := 0;
ExternalSize := To64(0);
SortFilesByName := False;
Sign := fsNoSetting;
@ -5258,7 +5264,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 +5351,15 @@ begin
Include(Options, foExternalSizePreset);
end;
{ DownloadISSigSource }
DownloadISSigSource := Values[paDownloadISSigSource].Data;
{ DownloadUserName }
DownloadUserName := Values[paDownloadUserName].Data;
{ DownloadPassword }
DownloadPassword := Values[paDownloadPassword].Data;
{ ExtractArchivePassword }
ExtractArchivePassword := Values[paExtractArchivePassword].Data;
@ -5417,7 +5433,7 @@ begin
end;
if (foGacInstall in Options) and (AStrongAssemblyName = '') then
AbortCompile(SCompilerFilesStrongAssemblyNameMustBeSpecified);
AbortCompileFmt(SCompilerParamFlagMissingParam, ['StrongAssemblyName', 'gacinstall']);
if AStrongAssemblyName <> '' then
StrongAssemblyName := AStrongAssemblyName;
@ -5431,6 +5447,25 @@ 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 foSkipIfSourceDoesntExist in Options then
AbortCompileFmt(SCompilerParamErrorBadCombo2, [ParamCommonFlags, 'download', 'skipifsourcedoesntexist'])
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 +5503,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);
@ -5485,8 +5520,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();

View File

@ -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;
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;

View File

@ -243,14 +243,15 @@ const
FilesSectionParameters: array of TScintRawString = [
'AfterInstall', 'Attribs', 'BeforeInstall', 'Check', 'Components', 'CopyMode',
'DestDir', 'DestName', 'Excludes', 'ExternalSize', 'ExtractArchivePassword',
'DestDir', 'DestName', 'DownloadISSigSource', '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',

View File

@ -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
@ -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;

View File

@ -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,9 +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(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;
const OnSimpleDownloadProgressParam: Integer64): Int64;
function DownloadTemporaryFile(const Url, BaseName, RequiredSHA256OfFile: String;
const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString;
const OnDownloadProgress: TOnDownloadProgress): Int64;
@ -44,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;
@ -73,11 +79,15 @@ begin
WizardForm.FilenameLabel.Update;
end;
procedure SetStatusLabelText(const S: String);
procedure SetStatusLabelText(const S: String;
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;
@ -108,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]);
@ -133,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);
@ -211,12 +219,30 @@ begin
if NeedToAbortInstall then Abort;
end;
procedure ExtractorProgressProc(Bytes: Cardinal);
procedure InternalProgressProc(const Bytes: Cardinal);
begin
IncProgress(Bytes);
ProcessEvents;
end;
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;
function AbortRetryIgnoreTaskDialogMsgBox(const Text: String;
const RetryIgnoreAbortButtonLabels: array of String): Boolean;
{ Returns True if Ignore was selected, False if Retry was selected, or
@ -327,7 +353,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;
@ -363,15 +388,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
@ -1001,7 +1018,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,7 +1262,11 @@ var
raise;
end;
{ Update the filename label }
{ Update the status and filename labels }
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
@ -1268,6 +1291,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
@ -1277,11 +1301,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
@ -1303,8 +1331,10 @@ var
if not(foIgnoreVersion in CurFile^.Options) then begin
AllowTimeStampComparison := False;
{ Read version info of file being installed }
if foDownload in CurFile^.Options then
InternalError('Unexpected Download flag');
if foExtractArchive in CurFile^.Options then
InternalError('Unexpected extractarchive flag');
InternalError('Unexpected ExtractArchive flag');
if Assigned(CurFileLocation) then begin
CurFileVersionInfoValid := floVersionInfoValid in CurFileLocation^.Flags;
CurFileVersionInfo.MS := CurFileLocation^.FileVersionMS;
@ -1405,6 +1435,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 }
@ -1532,16 +1564,47 @@ 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: 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);
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
extra .issig size has already been added to CurFile's ExternalSize. }
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);
try
{ Download the .issig file }
const ISSigUrl = GetISSigUrl(SourceFile, ExpandConst(CurFile^.DownloadISSigSource));
DownloadFile(ISSigUrl, DownloadUserName, DownloadPassword,
ISSigDestF, False, '', '', JustProcessEventsProc64, To64(0));
FreeAndNil(ISSigDestF);
{ Download and verify the actual file }
DownloadFile(SourceFile, DownloadUserName, DownloadPassword,
DestF, True, CurFile^.ISSigAllowedKeys, TempFile, ExternalProgressProc64, MaxProgress);
finally
ISSigDestF.Free;
{ Delete the .issig file }
DeleteFileRedir(DisableFsRedir, ISSigTempFile);
end;
end else
DownloadFile(SourceFile, DownloadUserName, DownloadPassword,
DestF, False, '', '', ExternalProgressProc64, MaxProgress);
end
else begin
{ Copy a duplicated non-external file, or an external file }
@ -2039,7 +2102,6 @@ var
I: Integer;
CurFileNumber: Integer;
CurFile: PSetupFileEntry;
ExternalSize: Integer64;
SourceWildcard: String;
ProgressBefore, ExpectedBytesLeft: Integer64;
DisableFsRedir, FoundFiles: Boolean;
@ -2078,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
@ -2097,7 +2157,17 @@ var
repeat
SetProgress(ProgressBefore);
ExpectedBytesLeft := CurFile^.ExternalSize;
if foExtractArchive in CurFile^.Options then
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 }
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, Excludes, CurFile,
ExpectedBytesLeft, ConfirmOverwriteOverwriteAll, PromptIfOlderOverwriteAll,
@ -3609,6 +3679,8 @@ type
private
FBaseName, FUrl: String;
FOnDownloadProgress: TOnDownloadProgress;
FOnSimpleDownloadProgress: TOnSimpleDownloadProgress;
FOnSimpleDownloadProgressParam: Integer64;
FAborted: Boolean;
FProgress, FProgressMax: Int64;
FLastReportedProgress, FLastReportedProgressMax: Int64;
@ -3616,6 +3688,8 @@ type
property BaseName: String write FBaseName;
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;
@ -3644,13 +3718,24 @@ 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
try
FOnSimpleDownloadProgress(Integer64(Progress-FLastReportedProgress), FOnSimpleDownloadProgressParam);
except
if ExceptObject is EAbort then begin
Abort := True;
FAborted := True;
end else
raise;
end;
FLastReportedProgress := Progress;
end;
if not Abort and DownloadTemporaryFileOrExtractArchiveProcessMessages then
Application.ProcessMessages;
if Abort then
FAborted := True
end;
procedure SetUserAgentAndSecureProtocols(const AHTTPClient: THTTPClient);
@ -3708,11 +3793,92 @@ 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;
function DownloadFile(const Url, CustomUserName, CustomPassword: String;
const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString;
const ISSigSourceFilename: String;
const OnSimpleDownloadProgress: TOnSimpleDownloadProgress;
const OnSimpleDownloadProgressParam: Integer64): Int64;
var
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,
CustomUserName, CustomPassword, User, Pass, CleanUrl);
{ Setup downloader }
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);
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, ISSigSourceFilename, 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;

View File

@ -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
@ -1811,7 +1810,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;
@ -1865,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,
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,
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;
@ -2772,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);
@ -2821,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,
@ -3517,6 +3522,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

View File

@ -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

View File

@ -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));

View File

@ -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;

View File

@ -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);

View File

@ -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
@ -243,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
@ -287,4 +287,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.

View File

@ -87,6 +87,7 @@ type
msgErrorCreatingTemp,
msgErrorDownloadAborted,
msgErrorDownloadFailed,
msgErrorDownloading,
msgErrorDownloadSizeFailed,
msgErrorExecutingProgram,
msgErrorExtracting,
@ -235,6 +236,7 @@ type
msgStatusCreateIcons,
msgStatusCreateIniEntries,
msgStatusCreateRegistryEntries,
msgStatusDownloadFiles,
msgStatusExtractFiles,
msgStatusRegisterFiles,
msgStatusRestartingApplications,

View File

@ -227,14 +227,14 @@ type
PublicX, PublicY, RuntimeID: String;
end;
const
SetupFileEntryStrings = 12;
SetupFileEntryStrings = 15;
SetupFileEntryAnsiStrings = 1;
type
PSetupFileEntry = ^TSetupFileEntry;
TSetupFileEntry = packed record
SourceFilename, DestName, InstallFontName, StrongAssemblyName, Components,
Tasks, Languages, Check, AfterInstall, BeforeInstall, Excludes,
ExtractArchivePassword: String;
DownloadISSigSource, 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

View File

@ -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

View File

@ -82,6 +82,26 @@ Source: "{tmp}\MyProg-ExtraReadmes.7z"; DestDir: "{app}"; \
</ul>
</li>
</ul>
<span class="head2">Improved file downloads</span>
<p>Support for downloading files has been improved: the <tt>[Files]</tt> 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.</p>
<ul>
<li>Updated <tt>[Files]</tt> section:
<ul>
<li>Added new <tt>[Files]</tt> section flag <tt>download</tt> and parameters <tt>DownloadUserName</tt> and <tt>DownloadPassword</tt>, intended to enable the seamless integration of downloaded files.</li>
<li>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.</li>
<li>Flag <tt>download</tt> must be combined with the <tt>external</tt> and <tt>ignoreversion</tt> flags. Additionally, the <tt>DestName</tt> and <tt>ExternalSize</tt> parameters must be set.<br/>
It can also be combined with the new <tt>issigverify</tt> flag (see below) for efficient and highly secure verification of the download file.<br/>
It cannot be combined with the <tt>comparetimestampalso</tt>, <tt>recursesubdirs</tt> and <tt>skipifsourcedoesntexist</tt> flags.</li>
<li>File download otherwise behaves the same as external file copying. For example, it supports automatic uninstallation of downloaded files and can be combined with same other flags and parameters.</li>
<li>Example script:
<pre>[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</pre>
</li>
<li>Updated example script <i>CodeDownloadFiles.iss</i> to demonstrate how to use a single <tt>[Files]</tt> entry to download a file.</li>
</ul>
</li>
</ul>
<span class="head2">New signature-verification capability</span>
<p>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.</p>
<p>Any type of file may be signed and verified, and creation of signatures does <i>not</i> require a certificate from a certificate authority. There is no cost involved.</p>
@ -184,13 +204,14 @@ issigtool --key-file="MyKey.ispublickey" verify "MyProg.dll"</pre>
<li>Minor tweaks.</li>
</ul>
<p>Some messages have been added and changed in this version: (<a href="https://github.com/jrsoftware/issrc/commit/0445cba192ed530ff5a701f35492ba1ecdabdeb8">View differences in Default.isl</a>.)</p>
<p>Some messages have been added and changed in this version: (<a href="https://github.com/jrsoftware/issrc/commit/f832ee26">View differences in Default.isl</a>.)</p>
<ul>
<li><b>New messages:</b>
<ul>
<li>ArchiveIncorrectPassword, ArchiveIsCorrupted, ArchiveUnsupportedFormat.</li>
<li>StatusDownloadFiles.</li>
<li>SourceVerificationFailed, VerificationSignatureDoesntExist, VerificationSignatureInvalid, VerificationKeyNotFound, VerificationFileSizeIncorrect, VerificationFileHashIncorrect.</li>
<li>ErrorExtracting.</li>
<li>ErrorDownloading, ErrorExtracting.</li>
</ul>
</li>
</ul>