Shared.SetupTypes.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. unit Shared.SetupTypes;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Types and functions used by both ISCmplr-only and Setup-only units
  8. }
  9. interface
  10. uses
  11. SysUtils, Classes, ECDSA, Shared.Struct;
  12. const
  13. { Predefined page identifiers }
  14. wpWelcome = 1;
  15. wpLicense = 2;
  16. wpPassword = 3;
  17. wpInfoBefore = 4;
  18. wpUserInfo = 5;
  19. wpSelectDir = 6;
  20. wpSelectComponents = 7;
  21. wpSelectProgramGroup = 8;
  22. wpSelectTasks = 9;
  23. wpReady = 10;
  24. wpPreparing = 11;
  25. wpInstalling = 12;
  26. wpInfoAfter = 13;
  27. wpFinished = 14;
  28. type
  29. TInstallOnThisVersionResult = (irInstall, irNotOnThisPlatform,
  30. irVersionTooLow, irServicePackTooLow, irVerTooHigh);
  31. TRenamedConstantCallBack = procedure(const Cnst, CnstRenamed: String) of object;
  32. TArrayOfECDSAKey = array of TECDSAKey;
  33. TISSigVerifySignatureError = (vseSignatureMissing, vseSignatureMalformed, vseKeyNotFound,
  34. vseSignatureBad, vseFileSizeIncorrect, vseFileHashIncorrect);
  35. const
  36. crHand = 1;
  37. CodeRootKeyFlagMask = $7F000000;
  38. CodeRootKeyFlag32Bit = $01000000;
  39. CodeRootKeyFlag64Bit = $02000000;
  40. CodeRootKeyValidFlags = CodeRootKeyFlag32Bit or CodeRootKeyFlag64Bit;
  41. HKEY_AUTO = 1; { Any value will work as long as it isn't 0 and doesn't match a predefined key handle (8xxxxxxx) nor includes any of the CodeRootKeyValidFlags flags. }
  42. function StringsToCommaString(const Strings: TStrings): String;
  43. procedure SetStringsFromCommaString(const Strings: TStrings; const Value: String);
  44. function StrToSetupVersionData(const S: String; var VerData: TSetupVersionData): Boolean;
  45. procedure HandleRenamedConstants(var Cnst: String; const RenamedConstantCallback: TRenamedConstantCallback);
  46. procedure GenerateEncryptionKey(const Password: String; const Salt: TSetupKDFSalt;
  47. const Iterations: Integer; out Key: TSetupEncryptionKey);
  48. procedure SetISSigAllowedKey(var ISSigAllowedKeys: AnsiString; const KeyIndex: Integer);
  49. function GetISSigAllowedKeys([ref] const ISSigAvailableKeys: TArrayOfECDSAKey;
  50. const ISSigAllowedKeys: AnsiString): TArrayOfECDSAKey;
  51. function IsExcluded(Text: String; const AExcludes: TStrings): Boolean;
  52. implementation
  53. uses
  54. PBKDF2, PathFunc, Shared.CommonFunc;
  55. function QuoteStringIfNeeded(const S: String): String;
  56. { Used internally by StringsToCommaString. Adds quotes around the string if
  57. needed, and doubles any embedded quote characters.
  58. Note: No lead byte checking is done since spaces/commas/quotes aren't used
  59. as trail bytes in any of the Far East code pages (CJK). }
  60. var
  61. Len, QuoteCount, I: Integer;
  62. HasSpecialChars: Boolean;
  63. P: PChar;
  64. begin
  65. Len := Length(S);
  66. HasSpecialChars := False;
  67. QuoteCount := 0;
  68. for I := 1 to Len do begin
  69. case S[I] of
  70. #0..' ', ',': HasSpecialChars := True;
  71. '"': Inc(QuoteCount);
  72. end;
  73. end;
  74. if not HasSpecialChars and (QuoteCount = 0) then begin
  75. Result := S;
  76. Exit;
  77. end;
  78. SetString(Result, nil, Len + QuoteCount + 2);
  79. P := Pointer(Result);
  80. P^ := '"';
  81. Inc(P);
  82. for I := 1 to Len do begin
  83. if S[I] = '"' then begin
  84. P^ := '"';
  85. Inc(P);
  86. end;
  87. P^ := S[I];
  88. Inc(P);
  89. end;
  90. P^ := '"';
  91. end;
  92. function StringsToCommaString(const Strings: TStrings): String;
  93. { Creates a comma-delimited string from Strings.
  94. Note: Unlike Delphi 2's TStringList.CommaText property, this function can
  95. handle an unlimited number of characters. }
  96. var
  97. I: Integer;
  98. S: String;
  99. begin
  100. if (Strings.Count = 1) and (Strings[0] = '') then
  101. Result := '""'
  102. else begin
  103. Result := '';
  104. for I := 0 to Strings.Count-1 do begin
  105. S := QuoteStringIfNeeded(Strings[I]);
  106. if I = 0 then
  107. Result := S
  108. else
  109. Result := Result + ',' + S;
  110. end;
  111. end;
  112. end;
  113. procedure SetStringsFromCommaString(const Strings: TStrings; const Value: String);
  114. { Replaces Strings with strings from the comma- or space-delimited Value.
  115. Note: No lead byte checking is done since spaces/commas/quotes aren't used
  116. as trail bytes in any of the Far East code pages (CJK).
  117. Also, this isn't bugged like Delphi 3+'s TStringList.CommaText property --
  118. SetStringsFromCommaString(..., 'a,') will add two items, not one. }
  119. var
  120. P, PStart, PDest: PChar;
  121. CharCount: Integer;
  122. S: String;
  123. begin
  124. Strings.BeginUpdate;
  125. try
  126. Strings.Clear;
  127. P := PChar(Value);
  128. while CharInSet(P^, [#1..' ']) do
  129. Inc(P);
  130. if P^ <> #0 then begin
  131. while True do begin
  132. if P^ = '"' then begin
  133. Inc(P);
  134. PStart := P;
  135. CharCount := 0;
  136. while P^ <> #0 do begin
  137. if P^ = '"' then begin
  138. Inc(P);
  139. if P^ <> '"' then Break;
  140. end;
  141. Inc(CharCount);
  142. Inc(P);
  143. end;
  144. P := PStart;
  145. SetString(S, nil, CharCount);
  146. PDest := Pointer(S);
  147. while P^ <> #0 do begin
  148. if P^ = '"' then begin
  149. Inc(P);
  150. if P^ <> '"' then Break;
  151. end;
  152. PDest^ := P^;
  153. Inc(P);
  154. Inc(PDest);
  155. end;
  156. end
  157. else begin
  158. PStart := P;
  159. while (P^ > ' ') and (P^ <> ',') do
  160. Inc(P);
  161. SetString(S, PStart, P - PStart);
  162. end;
  163. Strings.Add(S);
  164. while CharInSet(P^, [#1..' ']) do
  165. Inc(P);
  166. if P^ = #0 then
  167. Break;
  168. if P^ = ',' then begin
  169. repeat
  170. Inc(P);
  171. until not CharInSet(P^, [#1..' ']);
  172. end;
  173. end;
  174. end;
  175. finally
  176. Strings.EndUpdate;
  177. end;
  178. end;
  179. function StrToSetupVersionData(const S: String; var VerData: TSetupVersionData): Boolean;
  180. procedure Split(const Str: String; var Ver: TSetupVersionDataVersion;
  181. var ServicePack: Word);
  182. var
  183. I, J: Integer;
  184. Z, B: String;
  185. HasBuild: Boolean;
  186. begin
  187. Cardinal(Ver) := 0;
  188. ServicePack := 0;
  189. Z := Lowercase(Str);
  190. I := Pos('sp', Z);
  191. if I <> 0 then begin
  192. J := StrToInt(Copy(Z, I+2, Maxint));
  193. if (J < Low(Byte)) or (J > High(Byte)) then
  194. Abort;
  195. ServicePack := J shl 8;
  196. { ^ Shift left 8 bits because we're setting the "major" service pack
  197. version number. This parser doesn't currently accept "minor" service
  198. pack version numbers. }
  199. SetLength(Z, I-1);
  200. end;
  201. I := Pos('.', Z);
  202. if I = Length(Z) then Abort;
  203. if I <> 0 then begin
  204. J := StrToInt(Copy(Z, 1, I-1));
  205. if (J < 0) or (J > 127) then
  206. Abort;
  207. Ver.Major := J;
  208. Z := Copy(Z, I+1, Maxint);
  209. I := Pos('.', Z);
  210. HasBuild := I <> 0;
  211. if not HasBuild then
  212. I := Length(Z)+1;
  213. B := Copy(Z, I+1, Maxint);
  214. Z := Copy(Z, 1, I-1);
  215. J := StrToInt(Z);
  216. if (J < 0) or (J > 99) then Abort;
  217. Ver.Minor := J;
  218. if HasBuild then begin
  219. J := StrToInt(B);
  220. if (J < Low(Ver.Build)) or (J > High(Ver.Build)) then
  221. Abort;
  222. Ver.Build := J;
  223. end;
  224. end
  225. else begin { no minor version specified }
  226. J := StrToInt(Z);
  227. if (J < 0) or (J > 127) then
  228. Abort;
  229. Ver.Major := J;
  230. end;
  231. end;
  232. var
  233. I: Integer;
  234. SP: Word;
  235. begin
  236. try
  237. VerData.WinVersion := 0;
  238. I := Pos(',', S);
  239. if I <> 0 then begin
  240. Split(Trim(Copy(S, 1, I-1)),
  241. TSetupVersionDataVersion(VerData.WinVersion), SP);
  242. if SP <> 0 then Abort; { only NT has service packs }
  243. end;
  244. Split(Trim(Copy(S, I+1, Maxint)),
  245. TSetupVersionDataVersion(VerData.NTVersion), VerData.NTServicePack);
  246. Result := True;
  247. except
  248. if (ExceptObject is EAbort) or (ExceptObject is EConvertError) then
  249. Result := False
  250. else
  251. raise;
  252. end;
  253. end;
  254. procedure HandleRenamedConstants(var Cnst: String; const RenamedConstantCallback: TRenamedConstantCallback);
  255. var
  256. CnstRenamed: String;
  257. begin
  258. if Cnst = 'fonts' then
  259. CnstRenamed := 'commonfonts'
  260. else if Cnst = 'sendto' then
  261. CnstRenamed := 'usersendto'
  262. else if Cnst = 'pf' then
  263. CnstRenamed := 'commonpf'
  264. else if Cnst = 'pf32' then
  265. CnstRenamed := 'commonpf32'
  266. else if Cnst = 'pf64' then
  267. CnstRenamed := 'commonpf64'
  268. else if Cnst = 'cf' then
  269. CnstRenamed := 'commoncf'
  270. else if Cnst = 'cf32' then
  271. CnstRenamed := 'commoncf32'
  272. else if Cnst = 'cf64' then
  273. CnstRenamed := 'commoncf64'
  274. else
  275. CnstRenamed := '';
  276. if CnstRenamed <> '' then begin
  277. if Assigned(RenamedConstantCallback) then
  278. RenamedConstantCallback(Cnst, CnstRenamed);
  279. Cnst := CnstRenamed;
  280. end;
  281. end;
  282. procedure GenerateEncryptionKey(const Password: String; const Salt: TSetupKDFSalt;
  283. const Iterations: Integer; out Key: TSetupEncryptionKey);
  284. begin
  285. var SaltBytes: TBytes;
  286. var SaltSize := SizeOf(Salt);
  287. SetLength(SaltBytes, SaltSize);
  288. Move(Salt[0], SaltBytes[0], SaltSize);
  289. var KeyLength := SizeOf(Key);
  290. var KeyBytes := PBKDF2SHA256(Password, SaltBytes, Iterations, KeyLength);
  291. Move(KeyBytes[0], Key[0], KeyLength);
  292. end;
  293. procedure SetISSigAllowedKey(var ISSigAllowedKeys: AnsiString; const KeyIndex: Integer);
  294. { ISSigAllowedKeys should start out empty. If you then only use this function
  295. to update it, regular string comparison can be used for comparisons. }
  296. begin
  297. const ByteIndex = KeyIndex div 8;
  298. while ByteIndex >= Length(ISSigAllowedKeys) do
  299. ISSigAllowedKeys := ISSigAllowedKeys + #0;
  300. const BitIndex = KeyIndex mod 8;
  301. ISSigAllowedKeys[ByteIndex+1] := AnsiChar(Byte(ISSigAllowedKeys[ByteIndex+1]) or (1 shl BitIndex));
  302. end;
  303. function IsISSigAllowedKey(const ISSigAllowedKeys: AnsiString; const KeyIndex: Integer): Boolean;
  304. begin
  305. const ByteIndex = KeyIndex div 8;
  306. if ByteIndex >= Length(ISSigAllowedKeys) then
  307. Exit(False);
  308. const BitIndex = KeyIndex mod 8;
  309. Result := Byte(ISSigAllowedKeys[ByteIndex+1]) and (1 shl BitIndex) <> 0;
  310. end;
  311. function GetISSigAllowedKeys([ref] const ISSigAvailableKeys: TArrayOfECDSAKey;
  312. const ISSigAllowedKeys: AnsiString): TArrayOfECDSAKey;
  313. { Returns all keys if ISSigAllowedKeys is empty! }
  314. begin
  315. if ISSigAllowedKeys <> '' then begin
  316. const NAvailable = Length(ISSigAvailableKeys);
  317. SetLength(Result, NAvailable);
  318. var NAdded := 0;
  319. for var KeyIndex := 0 to NAvailable-1 do begin
  320. if IsISSigAllowedKey(ISSigAllowedKeys, KeyIndex) then begin
  321. Result[NAdded] := ISSigAvailableKeys[KeyIndex];
  322. Inc(NAdded);
  323. end;
  324. end;
  325. SetLength(Result, NAdded);
  326. end else
  327. Result := ISSigAvailableKeys;
  328. end;
  329. function IsExcluded(Text: String; const AExcludes: TStrings): Boolean;
  330. function CountBackslashes(S: PChar): Integer;
  331. begin
  332. Result := 0;
  333. while True do begin
  334. S := PathStrScan(S, '\');
  335. if S = nil then
  336. Break;
  337. Inc(Result);
  338. Inc(S);
  339. end;
  340. end;
  341. begin
  342. if AExcludes.Count > 0 then begin
  343. Text := PathLowercase(Text);
  344. UniqueString(Text);
  345. const T = PChar(Text);
  346. const TB = CountBackslashes(T);
  347. for var AExclude in AExcludes do begin
  348. var P := PChar(AExclude);
  349. { Leading backslash in an exclude pattern means 'match at the front
  350. instead of the end' }
  351. var MatchFront := False;
  352. if P^ = '\' then begin
  353. MatchFront := True;
  354. Inc(P);
  355. end;
  356. const PB = CountBackslashes(P);
  357. { The text must contain at least as many backslashes as the pattern
  358. for a match to be possible }
  359. if TB >= PB then begin
  360. var TStart := T;
  361. var TEnd: PChar;
  362. if not MatchFront then begin
  363. { If matching at the end, advance TStart so that TStart and P point
  364. to the same number of components }
  365. for var I := 1 to TB - PB do
  366. TStart := PathStrScan(TStart, '\') + 1;
  367. TEnd := nil;
  368. end
  369. else begin
  370. { If matching at the front, clip T to the same number of
  371. components as P }
  372. TEnd := T;
  373. for var J := 1 to PB do
  374. TEnd := PathStrScan(TEnd, '\') + 1;
  375. TEnd := PathStrScan(TEnd, '\');
  376. if Assigned(TEnd) then
  377. TEnd^ := #0;
  378. end;
  379. if WildcardMatch(TStart, P) then begin
  380. Result := True;
  381. Exit;
  382. end;
  383. { Put back any backslash that was temporarily null'ed }
  384. if Assigned(TEnd) then
  385. TEnd^ := '\';
  386. end;
  387. end;
  388. end;
  389. Result := False;
  390. end;
  391. end.