Shared.SetupTypes.pas 12 KB

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