diff --git a/Examples/CodeDownloadFiles.iss b/Examples/CodeDownloadFiles.iss index ca3fcabd..81f47178 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: 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); 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/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/ISHelp/isetup.xml b/ISHelp/isetup.xml index 4f933aa2..49f42665 100644 --- a/ISHelp/isetup.xml +++ b/ISHelp/isetup.xml @@ -1555,7 +1555,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.
@@ -1669,6 +1670,22 @@ ExternalSize: 1_048_576; 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, 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, 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, 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.
+ +This parameter is a set of extra options. Multiple options may be used by separating them by spaces. The following options are supported:
@@ -1712,14 +1729,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, 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 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.
@@ -1737,7 +1763,7 @@ Instructs Setup to proceed to comparing time stamps (last write/modified time) i 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:
When used without the external 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.
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.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.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 5e2886e4..d9e36f7d 100644
--- a/Projects/Src/Compiler.SetupCompiler.pas
+++ b/Projects/Src/Compiler.SetupCompiler.pas
@@ -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();
diff --git a/Projects/Src/Compression.SevenZipDLLDecoder.pas b/Projects/Src/Compression.SevenZipDLLDecoder.pas
index 6af92a8e..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;
- 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/IDE.ScintStylerInnoSetup.pas b/Projects/Src/IDE.ScintStylerInnoSetup.pas
index b64348d4..27afa35f 100644
--- a/Projects/Src/IDE.ScintStylerInnoSetup.pas
+++ b/Projects/Src/IDE.ScintStylerInnoSetup.pas
@@ -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',
diff --git a/Projects/Src/Setup.FileExtractor.pas b/Projects/Src/Setup.FileExtractor.pas
index 720e4660..743bb3be 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
@@ -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 8d6d4608..bbd8ce8d 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,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;
diff --git a/Projects/Src/Setup.MainFunc.pas b/Projects/Src/Setup.MainFunc.pas
index 688153fe..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
@@ -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
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 778e1cee..18fa2d54 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
@@ -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.
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..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 = 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
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
diff --git a/whatsnew.htm b/whatsnew.htm
index a4afd45d..6429bc69 100644
--- a/whatsnew.htm
+++ b/whatsnew.htm
@@ -82,6 +82,26 @@ Source: "{tmp}\MyProg-ExtraReadmes.7z"; DestDir: "{app}"; \
+Improved file downloads
+
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.
@@ -184,13 +204,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.)