winscpsetup.iss 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930
  1. #define AppId "winscp3"
  2. #define AppMutex "WinSCP"
  3. #define ParentRegistryKey "Software\Martin Prikryl"
  4. #define RegistryKey ParentRegistryKey+"\WinSCP 2"
  5. #define DefaultLang "en"
  6. #define WebRoot "https://winscp.net/"
  7. #define WebForum WebRoot+"forum/"
  8. #define WebDocumentation WebRoot+"eng/docs/"
  9. #define WebReport "https://winscp.net/install.php"
  10. #define Year 2024
  11. #define EnglishLang "English"
  12. #define SetupTypeData "SetupType"
  13. #define InnoSetupReg "Software\Microsoft\Windows\CurrentVersion\Uninstall\" + AppId + "_is1"
  14. #define InnoSetupAppPathReg "Inno Setup: App Path"
  15. #ifndef CompletenessThreshold
  16. #define CompletenessThreshold 100
  17. #else
  18. #define CompletenessThreshold Int(CompletenessThreshold)
  19. #endif
  20. #ifndef InclusionThreshold
  21. #define InclusionThreshold 100
  22. #else
  23. #define InclusionThreshold Int(InclusionThreshold)
  24. #endif
  25. #ifndef PuttySourceDir
  26. #if DirExists("c:\Program Files (x86)\PuTTY")
  27. #define PuttySourceDir "c:\Program Files (x86)\PuTTY"
  28. #else
  29. #define PuttySourceDir "c:\Program Files\PuTTY"
  30. #endif
  31. #endif
  32. #ifndef Status
  33. #define Status "unofficial"
  34. #endif
  35. #ifndef SourceDir
  36. #define SourceDir "..\source"
  37. #endif
  38. #ifndef BinariesDir
  39. #define BinariesDir SourceDir + "\Win32\Release"
  40. #endif
  41. #ifndef BinariesDir64
  42. #define BinariesDir64 SourceDir + "\Win64\Release"
  43. #endif
  44. #ifndef BinariesDirAssembly
  45. #define BinariesDirAssembly "..\dotnet\Win32\Release"
  46. #endif
  47. #ifndef AllTranslations
  48. #define AllTranslations
  49. #endif
  50. #define TranslationDir "translations"
  51. #define OutputDir "."
  52. #define TranslationFileMask "WinSCP.???"
  53. #define MainFileName "WinSCP.exe"
  54. #define MainFileSource BinariesDir+"\"+MainFileName
  55. #define ShellExtFileName "DragExt.dll"
  56. #define ShellExtFileSource BinariesDir+"\"+ShellExtFileName
  57. #define ShellExt64FileName "DragExt64.dll"
  58. #define ShellExt64FileSource BinariesDir64+"\"+ShellExt64FileName
  59. #define ConsoleFileSource BinariesDir+"\WinSCP.com"
  60. #define MapFileSource BinariesDir+"\WinSCP.map"
  61. #define AssemblyFileSource BinariesDirAssembly+"\WinSCPnet.dll"
  62. #ifdef Donations
  63. #define PayPalCardImage "PayPalCard.bmp"
  64. #endif
  65. #define Major
  66. #define Minor
  67. #define Rev
  68. #define Build
  69. #expr GetVersionComponents(MainFileSource, Major, Minor, Rev, Build)
  70. #define VersionOnly Str(Major)+"."+Str(Minor)+(Rev > 0 ? "."+Str(Rev) : "")
  71. #define Version VersionOnly+(Status != "" ? " "+Status : "")
  72. #ifndef BaseFilename
  73. #define FTag VersionOnly+(Status != "" ? "."+Status : "")
  74. #define BaseFilename "WinSCP-" + FTag + "-Setup"
  75. #endif
  76. #define WebArguments "ver=" +VersionOnly + "&lang={language}&utm_source=winscp&utm_medium=setup&utm_campaign=" + VersionOnly
  77. #define WebGettingStarted WebRoot + "eng/installed.php?" + WebArguments + "&prevver="
  78. #define MessagesPath(L) TranslationDir + "\" + "WinSCP." + L + ".islu"
  79. #define ExplorerFileBase "Explorer"
  80. #define CommanderFileBase "Commander"
  81. #define SelectDirFileBase "Opened bookmark folder-stored session folder"
  82. [Setup]
  83. AppId={#AppId}
  84. AppName=WinSCP
  85. AppPublisher=Martin Prikryl
  86. AppPublisherURL={#WebRoot}
  87. AppSupportURL={#WebForum}
  88. AppUpdatesURL={#WebRoot}eng/download.php
  89. VersionInfoCompany=Martin Prikryl
  90. VersionInfoDescription=Setup for WinSCP {#Version} (SFTP, FTP, WebDAV and SCP client)
  91. VersionInfoVersion={#Major}.{#Minor}.{#Rev}.{#Build}
  92. VersionInfoTextVersion={#Version}
  93. VersionInfoCopyright=(c) 2000-{#Year} Martin Prikryl
  94. VersionInfoOriginalFileName={#BaseFilename}.exe
  95. DefaultDirName={autopf}\WinSCP
  96. LicenseFile=license.setup.txt
  97. UninstallDisplayIcon={app}\WinSCP.exe
  98. OutputDir={#OutputDir}
  99. DisableStartupPrompt=yes
  100. AppVersion={#Version}
  101. AppVerName=WinSCP {#Version}
  102. OutputBaseFilename={#BaseFilename}
  103. SolidCompression=yes
  104. #ifdef ImagesDir
  105. WizardImageFile={#ImagesDir}\Tall *.bmp
  106. WizardSmallImageFile={#ImagesDir}\Square *.bmp
  107. #endif
  108. ShowTasksTreeLines=yes
  109. PrivilegesRequired=admin
  110. PrivilegesRequiredOverridesAllowed=commandline dialog
  111. ShowLanguageDialog=auto
  112. UsePreviousLanguage=no
  113. DisableProgramGroupPage=yes
  114. SetupIconFile=winscpsetup.ico
  115. DisableDirPage=no
  116. WizardStyle=modern
  117. ; We do not want the Explorer restarts as that is not pleasant to the user
  118. CloseApplications=no
  119. UsedUserAreasWarning=no
  120. #ifdef Sign
  121. SignTool=sign $f "WinSCP Installer" https://winscp.net/eng/docs/installation
  122. #endif
  123. [Languages]
  124. ; English has to be first so that it is pre-selected
  125. ; on Setup Select Language window, when no translation matching
  126. ; Windows UI locale is available
  127. Name: {#DefaultLang}; MessagesFile: {#MessagesPath(DefaultLang)}
  128. #define FindHandle
  129. #dim Languages[200]
  130. #define LanguageCount 0
  131. #define AnyLanguageComplete 0
  132. #define LangI
  133. ; For some reason the variable cannot be defined near the code where we use it
  134. #define AllTranslationsBuf
  135. #sub ProcessTranslationFile
  136. #define FileName FindGetFileName(FindHandle)
  137. #define Lang Copy(FileName, Pos(".", FileName)+1)
  138. #define LangNameFull ReadIni(MessagesPath(Lang), "LangOptions", "LanguageName")
  139. #define Sep Pos(" -", LangNameFull)
  140. #if Sep > 0
  141. #define LangName Copy(LangNameFull, 1, Sep - 1)
  142. #else
  143. #define LangName LangNameFull
  144. #endif
  145. #define LangID ReadIni(MessagesPath(Lang), "LangOptions", "LanguageID")
  146. #define LangCompleteness Int(ReadIni(MessagesPath(Lang), "CustomOptions", "TranslationCompleteness"))
  147. #expr Languages[LanguageCount*4] = Lang
  148. ; Not used atm
  149. #expr Languages[LanguageCount*4+1] = LangName
  150. ; Not used atm
  151. #expr Languages[LanguageCount*4+2] = LangID
  152. #expr Languages[LanguageCount*4+3] = LangCompleteness
  153. #expr LanguageCount++
  154. #if LangCompleteness >= CompletenessThreshold
  155. Name: {#Lang}; MessagesFile: {#MessagesPath(Lang)}
  156. #expr AnyLanguageComplete = 1
  157. #endif
  158. #endsub /* sub ProcessTranslationFile */
  159. #if FindHandle = FindFirst(TranslationDir + "\" + TranslationFileMask, 0)
  160. #define FResult 1
  161. #for {0; FResult; FResult = FindNext(FindHandle)} ProcessTranslationFile
  162. #expr FindClose(FindHandle)
  163. #endif
  164. ; Types are not used anymore, they are preserved only to let setup
  165. ; detect previous installation type and decide between typical/custom setup
  166. [Types]
  167. Name: full; Description: "full"
  168. Name: compact; Description: "compact"
  169. Name: custom; Description: "custom"; Flags: iscustom
  170. [Components]
  171. Name: main; Description: {cm:ApplicationComponent}; \
  172. Types: full custom compact; Flags: fixed
  173. ; Because the files for the component have Check parameters, they are ignored for the size calculation
  174. Name: shellext; Description: {cm:ShellExtComponent}; \
  175. ExtraDiskSpaceRequired: {#Max(FileSize(ShellExtFileSource), FileSize(ShellExt64FileSource))}; \
  176. Types: full compact;
  177. Name: pageant; Description: {cm:PageantComponent}; \
  178. Types: full
  179. Name: puttygen; Description: {cm:PuTTYgenComponent}; \
  180. Types: full
  181. #if AnyLanguageComplete == 1
  182. Name: transl; Description: {cm:TranslationsComponent}; \
  183. Types: full
  184. #endif
  185. [Tasks]
  186. Name: enableupdates; Description: {cm:EnableUpdates}
  187. Name: enableupdates\enablecollectusage; Description: {cm:EnableCollectUsage}
  188. ; Windows integration
  189. Name: desktopicon; Description: {cm:DesktopIconTask}
  190. Name: sendtohook; Description: {cm:SendToHookTask}
  191. Name: urlhandler; Description: {cm:RegisterAsUrlHandlers}
  192. Name: searchpath; Description: {cm:AddSearchPath}; \
  193. Flags: unchecked; Check: IsAdminInstallMode
  194. [Icons]
  195. Name: "{autoprograms}\WinSCP"; Filename: "{app}\WinSCP.exe"; Components: main; \
  196. Comment: "{cm:ProgramComment2}"
  197. ; This is created when desktopicon task is selected
  198. Name: "{autodesktop}\WinSCP"; Filename: "{app}\WinSCP.exe"; \
  199. Tasks: desktopicon; Comment: "{cm:ProgramComment2}"
  200. ; This is created when sendtohook task is selected
  201. Name: "{usersendto}\{cm:SendToHookNew}"; Filename: "{app}\WinSCP.exe"; \
  202. Parameters: "/upload"; Tasks: sendtohook
  203. [InstallDelete]
  204. ; Remove pre-5.8.2 PuTTY help file
  205. Type: files; Name: "{app}\PuTTY\putty.hlp"
  206. ; Remove pre-524 licence file (without .txt extension)
  207. Type: files; Name: "{app}\license"
  208. ; Remove pre-520 start menu folders
  209. Type: filesandordirs; Name: "{commonprograms}\WinSCP"
  210. Type: filesandordirs; Name: "{userprograms}\WinSCP"; Check: HasUserPrograms
  211. [Run]
  212. Filename: "{app}\WinSCP.exe"; Parameters: "/RegisterForDefaultProtocols"; \
  213. StatusMsg: {cm:RegisteringAsUrlHandlers}; Tasks: urlhandler
  214. Filename: "{app}\WinSCP.exe"; Parameters: "/AddSearchPath"; \
  215. StatusMsg: {cm:AddingSearchPath}; Tasks: searchpath
  216. Filename: "{app}\WinSCP.exe"; Parameters: "/ImportSitesIfAny"; \
  217. StatusMsg: {cm:ImportSites}; Flags: skipifsilent
  218. [UninstallDelete]
  219. ; These additional files are created by application
  220. Type: files; Name: "{app}\WinSCP.ini"
  221. Type: files; Name: "{app}\WinSCP.cgl"
  222. [Files]
  223. #ifdef ImagesDir
  224. ; Put these to the top as we extract them on demand and
  225. ; that can take long with solid compression enabled
  226. Source: "{#ImagesDir}\{#ExplorerFileBase} *.bmp"; Flags: dontcopy
  227. Source: "{#ImagesDir}\{#CommanderFileBase} *.bmp"; Flags: dontcopy
  228. Source: "{#ImagesDir}\{#SelectDirFileBase} *.bmp"; Flags: dontcopy
  229. #ifdef Donations
  230. Source: "{#ImagesDir}\{#PayPalCardImage}"; Flags: dontcopy
  231. #endif
  232. #endif
  233. Source: "{#MainFileSource}"; DestDir: "{app}"; \
  234. Components: main; Flags: ignoreversion
  235. Source: "{#ConsoleFileSource}"; DestDir: "{app}"; \
  236. Components: main; Flags: ignoreversion
  237. Source: "{#MapFileSource}"; DestDir: "{app}"; \
  238. Components: main; Flags: ignoreversion
  239. Source: "{#AssemblyFileSource}"; DestDir: "{app}"; \
  240. Components: main; Flags: ignoreversion
  241. Source: "license.txt"; DestDir: "{app}"; \
  242. Components: main; Flags: ignoreversion
  243. ; If the Check is ever removed, remove the ExtraDiskSpaceRequired parameter of the component too
  244. Source: "{#ShellExtFileSource}"; DestDir: "{app}"; \
  245. Components: shellext; \
  246. Flags: regserver restartreplace uninsrestartdelete ignoreversion; \
  247. Check: not IsWin64 and ShouldInstallShellExt(ExpandConstant('{app}\{#ShellExtFileName}'), '{#GetVersionNumbersString(ShellExtFileSource)}')
  248. Source: "{#ShellExt64FileSource}"; DestDir: "{app}"; \
  249. Components: shellext; \
  250. Flags: regserver restartreplace uninsrestartdelete ignoreversion; \
  251. Check: IsWin64 and ShouldInstallShellExt(ExpandConstant('{app}\{#ShellExt64FileName}'), '{#GetVersionNumbersString(ShellExt64FileSource)}')
  252. Source: "{#PuttySourceDir}\LICENCE"; DestDir: "{app}\PuTTY"; \
  253. Components: pageant puttygen; Flags: ignoreversion
  254. Source: "{#PuttySourceDir}\putty.chm"; DestDir: "{app}\PuTTY"; \
  255. Components: pageant puttygen; Flags: ignoreversion
  256. Source: "{#PuttySourceDir}\pageant.exe"; DestDir: "{app}\PuTTY"; \
  257. Components: pageant; Flags: ignoreversion
  258. Source: "{#PuttySourceDir}\puttygen.exe"; DestDir: "{app}\PuTTY"; \
  259. Components: puttygen; Flags: ignoreversion
  260. #ifdef ExtensionsDir
  261. Source: "{#ExtensionsDir}\*.*"; DestDir: "{app}\Extensions"
  262. #endif
  263. #ifdef Sponsor
  264. Source: "{#Sponsor}\*.*"; Flags: dontcopy skipifsourcedoesntexist
  265. #define SponsorImages
  266. #if FindHandle = FindFirst(Sponsor + "\*.*", 0)
  267. #define FResult 1
  268. #for {0; FResult; FResult = FindNext(FindHandle)} SponsorImages = SponsorImages + FindGetFileName(FindHandle) + ","
  269. #expr FindClose(FindHandle)
  270. #expr SponsorImages = Copy(SponsorImages, 1, Len(SponsorImages) - 1)
  271. #endif
  272. #endif
  273. [Registry]
  274. Root: HKCU; Subkey: "{#ParentRegistryKey}"; Flags: uninsdeletekeyifempty
  275. Root: HKCU; Subkey: "{#RegistryKey}"; Flags: uninsdeletekeyifempty
  276. ; Norton Commander interface
  277. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  278. ValueName: "Interface"; ValueData: 0; Check: UserSettings(1)
  279. Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
  280. ValueName: "DefaultInterfaceInterface"; ValueData: 0; \
  281. Check: UserSettings(1) and IsAdminInstallMode; Flags: noerror
  282. ; Explorer-like interface
  283. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  284. ValueName: "Interface"; ValueData: 1; Check: not UserSettings(1)
  285. Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
  286. ValueName: "DefaultInterfaceInterface"; ValueData: 1; \
  287. Check: (not UserSettings(1)) and IsAdminInstallMode; Flags: noerror
  288. ; If installer enabled ddext, let it reset the settings on uninstall,
  289. ; so the default is used on the next run
  290. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  291. ValueName: "DDExtEnabled"; ValueData: 1; Components: shellext; \
  292. Flags: uninsdeletevalue
  293. ; Updates
  294. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface\Updates"; \
  295. ValueType: dword; ValueName: "Period"; ValueData: 7; \
  296. Tasks: enableupdates; Check: not UpdatesEnabled
  297. Root: HKLM; SubKey: "{#RegistryKey}"; \
  298. ValueType: dword; ValueName: "DefaultUpdatesPeriod"; ValueData: 7; \
  299. Tasks: enableupdates; Flags: noerror; Check: IsAdminInstallMode
  300. Root: HKLM; SubKey: "{#RegistryKey}"; \
  301. ValueType: dword; ValueName: "DefaultCollectUsage"; ValueData: 1; \
  302. Tasks: enableupdates\enablecollectusage; Flags: noerror; Check: IsAdminInstallMode
  303. #sub EmitLang
  304. #if Languages[LangI*4+3] >= InclusionThreshold
  305. [Files]
  306. Source: "{#TranslationDir}\WinSCP.{#Languages[LangI*4]}"; DestDir: "{app}\Translations"; \
  307. Components: transl; Flags: ignoreversion
  308. #endif
  309. #endsub /* sub EmitLang */
  310. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang
  311. ; Delete translations from installation root folder (pre-5.10)
  312. [InstallDelete]
  313. #expr AllTranslationsBuf = AllTranslations + '-'
  314. #sub DeleteRootTranslation
  315. #define P Pos('-', AllTranslationsBuf)
  316. #define Lang Copy(AllTranslationsBuf, 1, P - 1)
  317. #expr AllTranslationsBuf = Copy(AllTranslationsBuf, P + 1)
  318. Type: files; Name: "{app}\WinSCP.{#Lang}"
  319. #endsub
  320. #for { 0; Len(AllTranslationsBuf) > 0; 0 } DeleteRootTranslation
  321. [UninstallRun]
  322. ; Make sure no later uninstall task recreate the configuration
  323. Filename: "{app}\WinSCP.exe"; Parameters: "/UninstallCleanup"; \
  324. RunOnceId: "UninstallCleanup"
  325. Filename: "{app}\WinSCP.exe"; Parameters: "/RemoveSearchPath"; \
  326. RunOnceId: "RemoveSearchPath"
  327. Filename: "{app}\WinSCP.exe"; Parameters: "/UnregisterForProtocols"; \
  328. RunOnceId: "UnregisterForProtocols"
  329. [Code]
  330. const
  331. NewLine = #13#10;
  332. var
  333. TypicalTypeButton: TRadioButton;
  334. CustomTypeButton: TRadioButton;
  335. CommanderRadioButton: TRadioButton;
  336. ExplorerRadioButton: TRadioButton;
  337. LaunchCheckbox: TCheckbox;
  338. OpenGettingStartedCheckbox: TCheckbox;
  339. AreUpdatesEnabled: Boolean;
  340. AutomaticUpdate: Boolean;
  341. Upgrade: Boolean;
  342. PrevVersion: string;
  343. ShellExtNewerCacheFileName: string;
  344. ShellExtNewerCacheResult: Boolean;
  345. ShellExtNoRestart: Boolean;
  346. #ifdef Donations
  347. DonationPanel: TPanel;
  348. AboutDonationCaption: TLabel;
  349. #endif
  350. InstallationDone: Boolean;
  351. LicenseAccepted: Boolean;
  352. InitDir: string;
  353. InitComponents: string;
  354. InitTasks: string;
  355. InitInterface: Integer;
  356. Donated: Boolean;
  357. InterfacePage: TWizardPage;
  358. SetupTypePage: TWizardPage;
  359. #ifdef Sponsor
  360. SponsorReq: Variant;
  361. SponsorPage: TWizardPage;
  362. Sponsor: string;
  363. SponsorStatus: string;
  364. #endif
  365. procedure ShowMessage(Text: string);
  366. begin
  367. MsgBox(Text, mbInformation, MB_OK);
  368. end;
  369. #ifdef Sponsor
  370. function IsWinVista: Boolean;
  371. begin
  372. Result := (GetWindowsVersion >= $06000000);
  373. end;
  374. #endif
  375. procedure CutVersionPart(var VersionString: string; var VersionPart: Word);
  376. var
  377. P: Integer;
  378. begin
  379. P := Pos('.', VersionString);
  380. if P > 0 then
  381. begin
  382. VersionPart := StrToIntDef(Copy(VersionString, 1, P - 1), 0);
  383. Delete(VersionString, 1, P);
  384. end
  385. else
  386. begin
  387. VersionPart := StrToIntDef(VersionString, 0);
  388. VersionString := '';
  389. end;
  390. end;
  391. function ShouldInstallShellExt(FileName: string; InstalledVersion: string): Boolean;
  392. var
  393. ExistingMS, ExistingLS: Cardinal;
  394. ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild: Cardinal;
  395. InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild: Word;
  396. begin
  397. if ShellExtNewerCacheFileName = FileName then
  398. begin
  399. if ShellExtNewerCacheResult then
  400. begin
  401. Log(Format('Allowing installation of shell extension %s as already decided', [FileName]));
  402. Result := True;
  403. end
  404. else
  405. begin
  406. Log(Format('Skipping installation of shell extension %s as already decided', [FileName]));
  407. Result := False;
  408. end;
  409. // Keeping ShellExtNoRestart value
  410. end
  411. else
  412. if not FileExists(FileName) then
  413. begin
  414. Log(Format('Shell extension %s does not exist yet, allowing installation', [FileName]));
  415. ShellExtNoRestart := False;
  416. Result := True;
  417. end
  418. else
  419. if not GetVersionNumbers(FileName, ExistingMS, ExistingLS) then
  420. begin
  421. Log(Format('Cannot retrieve version of existing shell extension %s, allowing installation', [FileName]));
  422. ShellExtNoRestart := False;
  423. Result := True;
  424. end
  425. else
  426. begin
  427. ExistingMajor := ExistingMS shr 16;
  428. ExistingMinor := ExistingMS and $FFFF;
  429. ExistingRev := ExistingLS shr 16;
  430. ExistingBuild := ExistingLS and $FFFF;
  431. Log(Format('Existing shell extension %s version: %d.%d.%d[.%d]', [FileName, ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild]));
  432. Log(Format('Installed extension version string: %s', [InstalledVersion]));
  433. CutVersionPart(InstalledVersion, InstalledMajor);
  434. CutVersionPart(InstalledVersion, InstalledMinor);
  435. CutVersionPart(InstalledVersion, InstalledRev);
  436. CutVersionPart(InstalledVersion, InstalledBuild);
  437. Log(Format('Installed extension version: %d.%d.%d[.%d]', [InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild]));
  438. if (InstalledMajor <> ExistingMajor) or
  439. ((ExistingMajor = 1) and (ExistingMinor <= 1)) then
  440. begin
  441. // Still on 1.x, so this won't be used when upgrading,
  442. // but it will be useful, if downgrading from future version with a different major version.
  443. if InstalledMajor <> ExistingMajor then
  444. begin
  445. Log('Existing extension has different major version, allowing installation, and will require restart, if it is locked.')
  446. end
  447. else
  448. begin
  449. // 1.1 uses Ansi encoding, and is incompatible with 1.2 and newer which uses Unicode
  450. Log('Existing extension is 1.1 or older, allowing installation, and will require restart, if it is locked.');
  451. end;
  452. Result := True;
  453. ShellExtNoRestart := False;
  454. end
  455. else
  456. if (InstalledMinor > ExistingMinor) or
  457. ((InstalledMinor = ExistingMinor) and (InstalledRev > ExistingRev)) then
  458. begin
  459. Log('Installed extension is newer than existing extension, but major version is the same, allowing installation, but we will delay replacing the extension until the next system start, if it is locked.');
  460. Result := True;
  461. ShellExtNoRestart := True;
  462. end
  463. else
  464. begin
  465. Log('Installed extension is same or older than existing extension (but the same major version), skipping installation');
  466. ShellExtNoRestart := False;
  467. Result := False;
  468. end;
  469. end;
  470. ShellExtNewerCacheFileName := FileName;
  471. ShellExtNewerCacheResult := Result;
  472. end;
  473. function UpdatesEnabled: Boolean;
  474. begin
  475. Result := AreUpdatesEnabled;
  476. end;
  477. function UserSettings(Settings: Integer): Boolean;
  478. begin
  479. case Settings of
  480. 1: Result := CommanderRadioButton.Checked;
  481. else Result := False;
  482. end;
  483. end;
  484. function LanguageCompleteness(Lang: string): Integer;
  485. begin
  486. #sub EmitLang4
  487. if (Lang = '{#Languages[LangI*4]}') then
  488. begin
  489. Result := {#Languages[LangI*4+3]};
  490. end
  491. else
  492. #endsub /* sub EmitLang4 */
  493. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang4
  494. // used also for the default language
  495. Result := 100;
  496. end;
  497. procedure OpenBrowser(Url: string);
  498. var
  499. ErrorCode: Integer;
  500. begin
  501. ShellExec('open', Url, '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
  502. end;
  503. function IsRestartPage: Boolean;
  504. begin
  505. Result := WizardForm.YesRadio.Visible;
  506. end;
  507. #ifdef Sponsor
  508. var
  509. SponsoringClicked: Boolean;
  510. procedure SponsoringLinkLabelClick(Sender: TObject);
  511. begin
  512. SponsoringClicked := True;
  513. OpenBrowser('{#WebReport}?mode=sponsoring' + Format('&sponsor=%s&', [Sponsor]) + ExpandConstant('{#WebArguments}'));
  514. end;
  515. #endif
  516. procedure OpenHelp;
  517. var
  518. HelpKeyword: string;
  519. begin
  520. HelpKeyword := 'ui_installer'; // default
  521. case WizardForm.CurPageID of
  522. wpLicense:
  523. HelpKeyword := 'ui_installer_license';
  524. wpSelectDir:
  525. HelpKeyword := 'ui_installer_selectdir';
  526. wpSelectComponents:
  527. HelpKeyword := 'ui_installer_selectcomponents';
  528. wpSelectTasks:
  529. HelpKeyword := 'ui_installer_selecttasks';
  530. wpReady:
  531. HelpKeyword := 'ui_installer_ready';
  532. wpFinished:
  533. HelpKeyword := 'ui_installer_finished';
  534. SetupTypePage.ID:
  535. HelpKeyword := 'ui_installer_setuptype';
  536. InterfacePage.ID:
  537. HelpKeyword := 'ui_installer_interface';
  538. #ifdef Sponsor
  539. SponsorPage.ID:
  540. begin
  541. SponsoringLinkLabelClick(nil);
  542. Exit;
  543. end;
  544. #endif
  545. end;
  546. OpenBrowser('{#WebDocumentation}' + HelpKeyword + '?' + ExpandConstant('{#WebArguments}'));
  547. end;
  548. procedure HelpButtonClick(Sender: TObject);
  549. begin
  550. OpenHelp;
  551. end;
  552. procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  553. begin
  554. if Key = 112 { VK_F1 } then
  555. begin
  556. OpenHelp;
  557. Key := 0;
  558. end;
  559. end;
  560. procedure CaptionClick(Sender: TObject);
  561. begin
  562. WizardForm.ActiveControl := TLabel(Sender).FocusControl;
  563. end;
  564. procedure ImageClick(Sender: TObject);
  565. begin
  566. WizardForm.ActiveControl := TWinControl(TControl(Sender).Tag);
  567. end;
  568. function WillRestart: Boolean;
  569. begin
  570. Result := WizardForm.YesRadio.Visible and WizardForm.YesRadio.Checked;
  571. end;
  572. procedure UpdatePostInstallRunCheckboxes(Sender: TObject);
  573. begin
  574. LaunchCheckbox.Enabled := not WillRestart;
  575. OpenGettingStartedCheckbox.Enabled :=
  576. LaunchCheckbox.Enabled
  577. end;
  578. procedure LinkLabel(Control: TLabel);
  579. begin
  580. Control.ParentFont := True;
  581. Control.Font.Style := Control.Font.Style + [fsUnderline];
  582. Control.Font.Color := clBlue;
  583. Control.Cursor := crHand;
  584. end;
  585. function IsTypicalInstallation: Boolean;
  586. begin
  587. Result := TypicalTypeButton.Checked;
  588. end;
  589. #ifdef Donations
  590. procedure AboutDonationsLinkClick(Sender: TObject);
  591. begin
  592. OpenBrowser('{#WebRoot}eng/donate.php?' + ExpandConstant('{#WebArguments}'));
  593. Donated := true;
  594. end;
  595. procedure DonateLinkClick(Sender: TObject);
  596. var
  597. Control: TControl;
  598. begin
  599. Control := TControl(Sender);
  600. OpenBrowser('{#WebRoot}eng/donate.php?amount=' + IntToStr(Control.Tag) + '&currency=' + CustomMessage('Currency') + '&' + ExpandConstant('{#WebArguments}'));
  601. Donated := true;
  602. end;
  603. procedure CreateDonateLink(Amount: Integer; var Top: Integer);
  604. var
  605. Caption: TLabel;
  606. begin
  607. Caption := TLabel.Create(DonationPanel);
  608. Caption.Left := 0;
  609. Caption.Top := Top;
  610. Caption.Tag := Amount;
  611. Caption.Parent := DonationPanel;
  612. Caption.Caption := Format(CustomMessage('Donate'), ['$' + IntToStr(Amount)]);
  613. Caption.OnClick := @DonateLinkClick;
  614. LinkLabel(Caption);
  615. Top := Top + ScaleY(16);
  616. end;
  617. #endif
  618. procedure LoadBitmap(Image: TBitmapImage; FileName: string);
  619. var
  620. Bitmap: TBitmap;
  621. begin
  622. Bitmap := TBitmap.Create();
  623. Bitmap.AlphaFormat := afDefined;
  624. Bitmap.LoadFromFile(FileName);
  625. Image.Bitmap := Bitmap;
  626. Bitmap.Free;
  627. end;
  628. procedure LoadEmbededBitmap(Image: TBitmapImage; Name: string);
  629. var
  630. FileName: string;
  631. begin
  632. ExtractTemporaryFile(Name);
  633. FileName := ExpandConstant('{tmp}\' + Name);
  634. LoadBitmap(Image, FileName);
  635. // we won't need this anymore
  636. DeleteFile(FileName);
  637. end;
  638. function GetScalingFactor: Integer;
  639. begin
  640. if WizardForm.Font.PixelsPerInch >= 192 then Result := 200
  641. else
  642. if WizardForm.Font.PixelsPerInch >= 144 then Result := 150
  643. else
  644. if WizardForm.Font.PixelsPerInch >= 120 then Result := 125
  645. else Result := 100;
  646. end;
  647. procedure LoadEmbededScaledIcon(Image: TBitmapImage; NameBase: string; SizeBase: Integer);
  648. var
  649. Name: String;
  650. begin
  651. Name := Format('%s %d.bmp', [NameBase, SizeBase * GetScalingFactor div 100]);
  652. LoadEmbededBitmap(Image, Name);
  653. Image.AutoSize := True;
  654. end;
  655. // WORKAROUND
  656. // Checkboxes and Radio buttons created on runtime do
  657. // not scale their height automatically
  658. procedure ScaleFixedHeightControl(Control: TButtonControl);
  659. begin
  660. Control.Height := ScaleY(Control.Height);
  661. end;
  662. function GetBottom(Control: TControl): Integer;
  663. begin
  664. Result := Control.Top + Control.Height;
  665. end;
  666. function GetRight(Control: TControl): Integer;
  667. begin
  668. Result := Control.Left + Control.Width;
  669. end;
  670. function CmdLineParamExists(const Value: string): Boolean;
  671. var
  672. I: Integer;
  673. begin
  674. Result := False;
  675. for I := 1 to ParamCount do
  676. begin
  677. if CompareText(ParamStr(I), Value) = 0 then
  678. begin
  679. Result := True;
  680. Exit;
  681. end;
  682. end;
  683. end;
  684. function InitializeSetup: Boolean;
  685. var
  686. WaitInterval: Integer;
  687. Wait: Integer;
  688. begin
  689. AutomaticUpdate := CmdLineParamExists('/AutomaticUpdate');
  690. if AutomaticUpdate then
  691. begin
  692. Log('Automatic update');
  693. Wait := 10000;
  694. end
  695. else
  696. begin
  697. Wait := 0;
  698. end;
  699. WaitInterval := 250;
  700. while (Wait > 0) and CheckForMutexes('{#AppMutex}') do
  701. begin
  702. Log('Application is still running, waiting');
  703. Sleep(WaitInterval);
  704. Wait := Wait - WaitInterval;
  705. end;
  706. while CheckForMutexes('{#AppMutex}') do
  707. begin
  708. if MsgBox(
  709. FmtMessage(SetupMessage(msgSetupAppRunningError), ['WinSCP']),
  710. mbError, MB_OKCANCEL) <> IDOK then
  711. begin
  712. Abort;
  713. end;
  714. end;
  715. if IsMsiProductInstalled('{029F9450-CFEF-4408-A2BB-B69ECE29EB18}', 0) and
  716. (not CmdLineParamExists('/OverrideMsi')) then
  717. begin
  718. MsgBox(CustomMessage('MsiInstallation'), mbError, MB_OK);
  719. Abort;
  720. end;
  721. Result := True;
  722. end;
  723. // Keep in sync with similar function on Preferences dialog
  724. function Bullet(S: string): string;
  725. begin
  726. if Copy(S, 1, 1) = '-' then S := #$2022' ' + Trim(Copy(S, 2, Length(S) - 1));
  727. Result := S;
  728. end;
  729. procedure InitializeWizard;
  730. var
  731. UserInterface: Cardinal;
  732. UpdatesPeriod: Cardinal;
  733. Caption: TLabel;
  734. #ifdef ImagesDir
  735. Image: TBitmapImage;
  736. #endif
  737. HelpButton: TButton;
  738. #ifdef Donations
  739. P: Integer;
  740. #ifdef ImagesDir
  741. P2: Integer;
  742. #endif
  743. #endif
  744. S: string;
  745. Completeness: Integer;
  746. begin
  747. InstallationDone := False;
  748. LicenseAccepted := False;
  749. InitInterface := -1;
  750. Upgrade :=
  751. // We may want to use HKA to work correctly with side-by-side installations.
  752. // But as Updade is really used for changing REGISTRY configuration options only,
  753. // which are shared between all-users and current-user installations, it does not really matter.
  754. RegQueryStringValue(HKLM, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S) or
  755. RegQueryStringValue(HKCU, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S);
  756. if Upgrade then
  757. begin
  758. Log('Upgrade');
  759. end
  760. else
  761. begin
  762. Log('New installation');
  763. end;
  764. if Upgrade and GetVersionNumbersString(AddBackslash(WizardDirValue) + '{#MainFileName}', PrevVersion) and
  765. (PrevVersion[2] = '.') and (PrevVersion[4] = '.') and (PrevVersion[6] = '.') then
  766. begin
  767. PrevVersion := Copy(PrevVersion, 1, 5);
  768. end;
  769. WizardForm.KeyPreview := True;
  770. WizardForm.OnKeyDown := @FormKeyDown;
  771. // allow installation without requiring user to accept license
  772. WizardForm.LicenseAcceptedRadio.Checked := True;
  773. WizardForm.LicenseAcceptedRadio.Visible := False;
  774. WizardForm.LicenseNotAcceptedRadio.Visible := False;
  775. WizardForm.LicenseMemo.Height :=
  776. GetBottom(WizardForm.LicenseNotAcceptedRadio) -
  777. WizardForm.LicenseMemo.Top - ScaleY(5);
  778. // hide installation types combo
  779. WizardForm.TypesCombo.Visible := False;
  780. WizardForm.ComponentsList.Height :=
  781. GetBottom(WizardForm.ComponentsList) -
  782. WizardForm.TypesCombo.Top;
  783. WizardForm.ComponentsList.Top := WizardForm.TypesCombo.Top;
  784. // add help button
  785. HelpButton := TButton.Create(WizardForm);
  786. HelpButton.Parent := WizardForm;
  787. HelpButton.Anchors := [akLeft, akBottom];
  788. HelpButton.Left := WizardForm.ClientWidth - GetRight(WizardForm.CancelButton);
  789. HelpButton.Top := WizardForm.CancelButton.Top;
  790. HelpButton.Width := WizardForm.CancelButton.Width;
  791. HelpButton.Height := WizardForm.CancelButton.Height;
  792. HelpButton.Caption := CustomMessage('HelpButton');
  793. HelpButton.OnClick := @HelpButtonClick;
  794. Completeness := LanguageCompleteness(ActiveLanguage);
  795. if (Completeness < 100) and (not WizardSilent) then
  796. begin
  797. ShowMessage(FmtMessage(CustomMessage('IncompleteTranslation'), [IntToStr(Completeness)]));
  798. end;
  799. // installation type page
  800. SetupTypePage := CreateCustomPage(wpLicense,
  801. CustomMessage('SetupTypeTitle'),
  802. CustomMessage('SetupTypePrompt'));
  803. TypicalTypeButton := TRadioButton.Create(SetupTypePage);
  804. if not Upgrade then
  805. S := CustomMessage('TypicalType')
  806. else
  807. S := CustomMessage('TypicalUpgradeType');
  808. TypicalTypeButton.Caption :=
  809. FmtMessage(CustomMessage('Recommended'), [S]);
  810. // check typical install, if typical install was installed before or
  811. // when version without setup type support was installed with
  812. // "full" installation or when there were no installation before
  813. // ("full" installation is default)
  814. TypicalTypeButton.Checked :=
  815. ((GetPreviousData('{#SetupTypeData}', '') = 'typical')) or
  816. ((GetPreviousData('{#SetupTypeData}', '') = '') and
  817. (WizardSetupType(False) = 'full'));
  818. TypicalTypeButton.Left := ScaleX(4);
  819. TypicalTypeButton.Width := SetupTypePage.SurfaceWidth -
  820. TypicalTypeButton.Left;
  821. ScaleFixedHeightControl(TypicalTypeButton);
  822. TypicalTypeButton.Parent := SetupTypePage.Surface;
  823. Caption := TLabel.Create(SetupTypePage);
  824. Caption.WordWrap := True;
  825. if not Upgrade then
  826. begin
  827. Caption.Caption :=
  828. Bullet(CustomMessage('TypicalType1')) + NewLine +
  829. Bullet(CustomMessage('TypicalType2')) + NewLine +
  830. Bullet(CustomMessage('TypicalType3'));
  831. end
  832. else
  833. begin
  834. Caption.Caption :=
  835. Bullet(CustomMessage('TypicalUpgradeType1'));
  836. end;
  837. Caption.Left := ScaleX(4) + ScaleX(20);
  838. Caption.Width := SetupTypePage.SurfaceWidth - Caption.Left;
  839. Caption.Top := GetBottom(TypicalTypeButton) + ScaleY(6);
  840. Caption.Parent := SetupTypePage.Surface;
  841. Caption.FocusControl := TypicalTypeButton;
  842. Caption.OnClick := @CaptionClick;
  843. CustomTypeButton := TRadioButton.Create(SetupTypePage);
  844. if not Upgrade then
  845. CustomTypeButton.Caption := CustomMessage('CustomType')
  846. else
  847. CustomTypeButton.Caption := CustomMessage('CustomUpgradeType');
  848. CustomTypeButton.Checked := (not TypicalTypeButton.Checked);
  849. CustomTypeButton.Left := ScaleX(4);
  850. CustomTypeButton.Width := SetupTypePage.SurfaceWidth -
  851. CustomTypeButton.Left;
  852. CustomTypeButton.Top := GetBottom(Caption) + ScaleY(10);
  853. ScaleFixedHeightControl(CustomTypeButton);
  854. CustomTypeButton.Parent := SetupTypePage.Surface;
  855. Caption := TLabel.Create(SetupTypePage);
  856. Caption.WordWrap := True;
  857. if not Upgrade then
  858. begin
  859. Caption.Caption :=
  860. Bullet(CustomMessage('CustomType1'));
  861. end
  862. else
  863. begin
  864. Caption.Caption :=
  865. Bullet(CustomMessage('CustomUpgradeType1')) + NewLine +
  866. Bullet(CustomMessage('CustomUpgradeType2'));
  867. end;
  868. Caption.Left := ScaleX(4) + ScaleX(20);
  869. Caption.Width := SetupTypePage.SurfaceWidth - Caption.Left;
  870. Caption.Top := GetBottom(CustomTypeButton) + ScaleY(6);
  871. Caption.Parent := SetupTypePage.Surface;
  872. Caption.FocusControl := CustomTypeButton;
  873. Caption.OnClick := @CaptionClick;
  874. // interface page
  875. InterfacePage := CreateCustomPage(wpSelectTasks,
  876. CustomMessage('UserSettingsTitle'),
  877. CustomMessage('UserSettingsPrompt'));
  878. UpdatesPeriod := 0;
  879. RegQueryDWordValue(HKCU, '{#RegistryKey}\Configuration\Interface\Updates',
  880. 'Period', UpdatesPeriod);
  881. AreUpdatesEnabled := (UpdatesPeriod <> 0);
  882. UserInterface := 0; { default is commander }
  883. RegQueryDWordValue(HKCU, '{#RegistryKey}\Configuration\Interface',
  884. 'Interface', UserInterface);
  885. Caption := TLabel.Create(InterfacePage);
  886. Caption.Caption := CustomMessage('UserInterfaceStyle');
  887. Caption.Width := InterfacePage.SurfaceWidth;
  888. Caption.Parent := InterfacePage.Surface;
  889. CommanderRadioButton := TRadioButton.Create(InterfacePage);
  890. CommanderRadioButton.Caption := CustomMessage('NortonCommanderInterfaceC');
  891. CommanderRadioButton.Checked := (UserInterface = 0);
  892. CommanderRadioButton.Left := ScaleX(4);
  893. CommanderRadioButton.Width := ScaleX(116);
  894. CommanderRadioButton.Top := GetBottom(Caption) + ScaleY(6);
  895. ScaleFixedHeightControl(CommanderRadioButton);
  896. CommanderRadioButton.Parent := InterfacePage.Surface;
  897. #ifdef ImagesDir
  898. Image := TBitmapImage.Create(InterfacePage);
  899. Image.Top := GetBottom(CommanderRadioButton) + ScaleY(6);
  900. Image.Left := CommanderRadioButton.Left + ScaleX(45);
  901. Image.Parent := InterfacePage.Surface;
  902. LoadEmbededScaledIcon(Image, '{#CommanderFileBase}', 32);
  903. Image.OnClick := @ImageClick;
  904. Image.Tag := Integer(CommanderRadioButton);
  905. #endif
  906. Caption := TLabel.Create(InterfacePage);
  907. Caption.WordWrap := True;
  908. Caption.Caption :=
  909. Bullet(CustomMessage('NortonCommanderInterface1')) + NewLine +
  910. Bullet(CustomMessage('NortonCommanderInterface2')) + NewLine +
  911. Bullet(CustomMessage('NortonCommanderInterface3'));
  912. Caption.Anchors := [akLeft, akTop, akRight];
  913. Caption.Left := GetRight(CommanderRadioButton);
  914. Caption.Width := InterfacePage.SurfaceWidth - Caption.Left;
  915. Caption.Top := CommanderRadioButton.Top;
  916. Caption.Parent := InterfacePage.Surface;
  917. Caption.FocusControl := CommanderRadioButton;
  918. Caption.OnClick := @CaptionClick;
  919. ExplorerRadioButton := TRadioButton.Create(InterfacePage);
  920. ExplorerRadioButton.Caption := CustomMessage('ExplorerInterfaceC');
  921. ExplorerRadioButton.Checked := (UserInterface <> 0);
  922. ExplorerRadioButton.Left := ScaleX(4);
  923. ExplorerRadioButton.Width := CommanderRadioButton.Width;
  924. ExplorerRadioButton.Top := GetBottom(Caption) + ScaleY(10);
  925. ScaleFixedHeightControl(ExplorerRadioButton);
  926. ExplorerRadioButton.Parent := InterfacePage.Surface;
  927. #ifdef ImagesDir
  928. Image := TBitmapImage.Create(InterfacePage);
  929. Image.Top := GetBottom(ExplorerRadioButton) + ScaleY(6);
  930. Image.Left := ExplorerRadioButton.Left + ScaleX(45);
  931. Image.Parent := InterfacePage.Surface;
  932. LoadEmbededScaledIcon(Image, '{#ExplorerFileBase}', 32);
  933. Image.OnClick := @ImageClick;
  934. Image.Tag := Integer(ExplorerRadioButton);
  935. #endif
  936. Caption := TLabel.Create(InterfacePage);
  937. Caption.WordWrap := True;
  938. Caption.Caption :=
  939. Bullet(CustomMessage('ExplorerInterface1')) + NewLine +
  940. Bullet(CustomMessage('ExplorerInterface2')) + NewLine +
  941. Bullet(CustomMessage('ExplorerInterface3'));
  942. Caption.Anchors := [akLeft, akTop, akRight];
  943. Caption.Left := GetRight(ExplorerRadioButton);
  944. Caption.Width := InterfacePage.SurfaceWidth - Caption.Left;
  945. Caption.Top := ExplorerRadioButton.Top;
  946. Caption.Parent := InterfacePage.Surface;
  947. Caption.FocusControl := ExplorerRadioButton;
  948. Caption.OnClick := @CaptionClick;
  949. // run checkbox
  950. LaunchCheckbox := TCheckbox.Create(WizardForm.FinishedPage);
  951. LaunchCheckbox.Caption := CustomMessage('Launch');
  952. LaunchCheckbox.Checked := True;
  953. LaunchCheckbox.Left := WizardForm.YesRadio.Left;
  954. LaunchCheckbox.Width := WizardForm.YesRadio.Width;
  955. ScaleFixedHeightControl(LaunchCheckbox);
  956. LaunchCheckbox.Parent := WizardForm.FinishedPage;
  957. OpenGettingStartedCheckbox := TCheckbox.Create(WizardForm.FinishedPage);
  958. OpenGettingStartedCheckbox.Caption := CustomMessage('OpenGettingStarted');
  959. OpenGettingStartedCheckbox.Checked := True;
  960. OpenGettingStartedCheckbox.Left := WizardForm.YesRadio.Left;
  961. OpenGettingStartedCheckbox.Width := WizardForm.YesRadio.Width;
  962. ScaleFixedHeightControl(OpenGettingStartedCheckbox);
  963. OpenGettingStartedCheckbox.Parent := WizardForm.FinishedPage;
  964. #ifdef Donations
  965. DonationPanel := TPanel.Create(WizardForm.FinishedPage);
  966. DonationPanel.Left := WizardForm.YesRadio.Left;
  967. DonationPanel.Width := WizardForm.YesRadio.Width;
  968. DonationPanel.Parent := WizardForm.FinishedPage;
  969. DonationPanel.BevelInner := bvNone;
  970. DonationPanel.BevelOuter := bvNone;
  971. DonationPanel.Color := WizardForm.FinishedPage.Color;
  972. Caption := TLabel.Create(DonationPanel);
  973. Caption.WordWrap := True;
  974. Caption.Caption := CustomMessage('PleaseDonate');
  975. Caption.Anchors := [akLeft, akTop, akRight];
  976. Caption.Left := 0;
  977. Caption.Top := 0;
  978. Caption.Width := DonationPanel.Width;
  979. Caption.Parent := DonationPanel;
  980. P := GetBottom(Caption) + ScaleY(12);
  981. #ifdef ImagesDir
  982. P2 := P;
  983. #endif
  984. CreateDonateLink( 9, P);
  985. CreateDonateLink(19, P);
  986. CreateDonateLink(49, P);
  987. AboutDonationCaption := TLabel.Create(DonationPanel);
  988. AboutDonationCaption.Left := 0;
  989. AboutDonationCaption.Top := P;
  990. AboutDonationCaption.Parent := DonationPanel;
  991. AboutDonationCaption.Caption := CustomMessage('AboutDonations');
  992. AboutDonationCaption.OnClick := @AboutDonationsLinkClick;
  993. LinkLabel(AboutDonationCaption);
  994. #ifdef ImagesDir
  995. Image := TBitmapImage.Create(DonationPanel);
  996. LoadEmbededBitmap(Image, '{#PayPalCardImage}');
  997. Image.BackColor := DonationPanel.Color;
  998. Image.AutoSize := True;
  999. Image.Cursor := crHand;
  1000. Image.Parent := DonationPanel;
  1001. Image.Left := ScaleX(108);
  1002. Image.Top := P2 + ScaleX(8);
  1003. Image.Hint := CustomMessage('AboutDonations');
  1004. Image.ShowHint := True;
  1005. Image.OnClick := @AboutDonationsLinkClick;
  1006. #endif
  1007. DonationPanel.Height := GetBottom(AboutDonationCaption);
  1008. #endif
  1009. WizardForm.YesRadio.OnClick := @UpdatePostInstallRunCheckboxes;
  1010. WizardForm.NoRadio.OnClick := @UpdatePostInstallRunCheckboxes;
  1011. UpdatePostInstallRunCheckboxes(nil);
  1012. #ifdef ImagesDir
  1013. // Text does not scale as quick as with DPI,
  1014. // so the icon may overlap the labels. Shift them.
  1015. P := WizardForm.SelectDirBitmapImage.Width;
  1016. LoadEmbededScaledIcon(WizardForm.SelectDirBitmapImage, '{#SelectDirFileBase}', 32);
  1017. P := (WizardForm.SelectDirBitmapImage.Width - P);
  1018. // Vertical change should be the same as horizontal
  1019. WizardForm.SelectDirLabel.Left := WizardForm.SelectDirLabel.Left + P;
  1020. WizardForm.SelectDirBrowseLabel.Top := WizardForm.SelectDirBrowseLabel.Top + P;
  1021. WizardForm.DirEdit.Top := WizardForm.DirEdit.Top + P;
  1022. WizardForm.DirBrowseButton.Top := WizardForm.DirBrowseButton.Top + P;
  1023. #endif
  1024. end;
  1025. procedure RegisterPreviousData(PreviousDataKey: Integer);
  1026. var
  1027. S: string;
  1028. begin
  1029. if IsTypicalInstallation then S := 'typical'
  1030. else S := 'custom';
  1031. SetPreviousData(PreviousDataKey, '{#SetupTypeData}', S);
  1032. end;
  1033. function SaveCheckListBoxState(ListBox: TNewCheckListBox): string;
  1034. var
  1035. I: Integer;
  1036. begin
  1037. for I := 0 to ListBox.Items.Count - 1 do
  1038. begin
  1039. Result := Result + IntToStr(Integer(ListBox.State[I]));
  1040. end;
  1041. end;
  1042. procedure CurPageChanged(CurPageID: Integer);
  1043. var
  1044. Delta: Integer;
  1045. LineHeight: Integer;
  1046. LaunchCheckboxTop: Integer;
  1047. S: string;
  1048. #ifdef Sponsor
  1049. SponsorQueryUrl: string;
  1050. PreferredSponsor: string;
  1051. #endif
  1052. begin
  1053. if CurPageID = wpLicense then
  1054. begin
  1055. WizardForm.NextButton.Caption := CustomMessage('AcceptButton')
  1056. end;
  1057. if CurPageID = wpSelectDir then
  1058. begin
  1059. if InitDir = '' then
  1060. InitDir := WizardForm.DirEdit.Text;
  1061. end
  1062. else
  1063. if CurPageID = wpSelectComponents then
  1064. begin
  1065. if InitComponents = '' then
  1066. InitComponents := SaveCheckListBoxState(WizardForm.ComponentsList);
  1067. end
  1068. else
  1069. if CurPageID = wpSelectTasks then
  1070. begin
  1071. if InitTasks = '' then
  1072. InitTasks := SaveCheckListBoxState(WizardForm.TasksList);
  1073. end
  1074. else
  1075. if CurPageID = InterfacePage.ID then
  1076. begin
  1077. if InitInterface < 0 then
  1078. InitInterface := Integer(CommanderRadioButton.Checked);
  1079. end
  1080. else
  1081. if CurPageID = wpFinished then
  1082. begin
  1083. LineHeight := (WizardForm.NoRadio.Top - WizardForm.YesRadio.Top);
  1084. // Are we at the "Restart?" screen
  1085. // Note that it's not possible to get to the "finished" page more than once,
  1086. // so the code below does not expect re-entry
  1087. if IsRestartPage then
  1088. begin
  1089. if ShellExtNoRestart then
  1090. begin
  1091. Log('Hiding restart page as it''s not critical to replace the shell extension');
  1092. WizardForm.YesRadio.Visible := False;
  1093. WizardForm.NoRadio.Visible := False;
  1094. WizardForm.NoRadio.Checked := True;
  1095. S := SetupMessage(msgFinishedLabel);
  1096. StringChange(S, '[name]', 'WinSCP');
  1097. WizardForm.FinishedLabel.Caption :=
  1098. S + NewLine + NewLine +
  1099. // The additional new line is a padding for the "launch check box",
  1100. // as the same padding is there for the YesRadio too.
  1101. SetupMessage(msgClickFinish) + NewLine;
  1102. Log(WizardForm.FinishedLabel.Caption);
  1103. Delta := WizardForm.AdjustLabelHeight(WizardForm.FinishedLabel);
  1104. LaunchCheckboxTop := WizardForm.YesRadio.Top + Delta;
  1105. end
  1106. else
  1107. begin
  1108. WizardForm.FinishedLabel.Caption :=
  1109. CustomMessage('FinishedRestartDragExtLabel') + NewLine;
  1110. Delta := WizardForm.AdjustLabelHeight(WizardForm.FinishedLabel);
  1111. WizardForm.YesRadio.Top := WizardForm.YesRadio.Top + Delta;
  1112. WizardForm.NoRadio.Top := WizardForm.NoRadio.Top + Delta;
  1113. LaunchCheckboxTop := WizardForm.NoRadio.Top + LineHeight;
  1114. #ifdef Donations
  1115. DonationPanel.Visible := False;
  1116. #endif
  1117. end;
  1118. end
  1119. else
  1120. begin
  1121. LaunchCheckboxTop := WizardForm.RunList.Top;
  1122. end;
  1123. LaunchCheckbox.Top := LaunchCheckboxTop;
  1124. OpenGettingStartedCheckbox.Top := LaunchCheckbox.Top + LineHeight;
  1125. UpdatePostInstallRunCheckboxes(nil);
  1126. #ifdef Donations
  1127. if DonationPanel.Visible then
  1128. begin
  1129. DonationPanel.Top := GetBottom(OpenGettingStartedCheckbox) + ScaleY(12);
  1130. // Hide "about donations" if it does not fit nicely
  1131. // (happens on "long" languages, as German)
  1132. // Should not happen anymore with "modern" style of IS6.
  1133. if (DonationPanel.Top + GetBottom(AboutDonationCaption)) >
  1134. (WizardForm.FinishedPage.Height - ScaleY(8)) then
  1135. begin
  1136. AboutDonationCaption.Visible := False;
  1137. end;
  1138. end;
  1139. #endif
  1140. end
  1141. else
  1142. if CurPageID = SetupTypePage.ID then
  1143. begin
  1144. Log('License accepted');
  1145. LicenseAccepted := True;
  1146. #ifdef Sponsor
  1147. if VarIsEmpty(SponsorReq) then
  1148. begin
  1149. // Need Vista for SHCONTCH_* constants
  1150. if WizardSilent or CmdLineParamExists('/NoSponsor') or (not IsWinVista) then
  1151. begin
  1152. Log('Skipping sponsor query request');
  1153. SponsorStatus := 'N';
  1154. end
  1155. else
  1156. begin
  1157. SponsorPage :=
  1158. CreateCustomPage(wpInstalling, 'Release sponsor', 'Please read a message from the sponsor of this release.');
  1159. SponsorQueryUrl :=
  1160. '{#WebReport}?' +
  1161. Format('mode=sponsorrequest&ver=%s&lang=%s&prevver=%s&scale=%d&images=%s', [
  1162. '{#VersionOnly}', ActiveLanguage, PrevVersion, GetScalingFactor, '{#SponsorImages}']);
  1163. PreferredSponsor := ExpandConstant('{param:Sponsor}');
  1164. if PreferredSponsor <> '' then
  1165. begin
  1166. SponsorQueryUrl := SponsorQueryUrl + Format('&sponsor=%s', [PreferredSponsor]);
  1167. end;
  1168. Log('Sending sponsor query request: ' + SponsorQueryUrl);
  1169. SponsorReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
  1170. SponsorReq.Open('GET', SponsorQueryUrl, True);
  1171. SponsorReq.Send('');
  1172. end;
  1173. end;
  1174. #endif
  1175. end;
  1176. end;
  1177. function AskedRestart: Boolean;
  1178. begin
  1179. Result := IsRestartPage;
  1180. end;
  1181. procedure DeinitializeSetup;
  1182. var
  1183. WinHttpReq: Variant;
  1184. ReportUrl: string;
  1185. ReportData: string;
  1186. begin
  1187. // cannot send report, unless user already accepted license
  1188. // (with privacy policy)
  1189. if LicenseAccepted then
  1190. begin
  1191. Log('Preparing installation report');
  1192. ReportData := Format(
  1193. 'mode=report&installed=%d&silent=%d&ver=%s&lang=%s&prevver=%s&', [
  1194. Integer(InstallationDone), Integer(WizardSilent),
  1195. '{#VersionOnly}', ActiveLanguage,
  1196. PrevVersion]);
  1197. #ifdef Sponsor
  1198. ReportData := ReportData +
  1199. Format('sponsorstatus=%s&sponsor=%s&sponsoringclicked=%d&', [SponsorStatus, Sponsor, Integer(SponsoringClicked)]);
  1200. #endif
  1201. try
  1202. ReportUrl := '{#WebReport}?' + ReportData;
  1203. Log('Sending installation report: ' + ReportUrl);
  1204. WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
  1205. WinHttpReq.Open('GET', ReportUrl, False);
  1206. WinHttpReq.Send('');
  1207. Log('Installation report send result: ' + IntToStr(WinHttpReq.Status) + ' ' + WinHttpReq.StatusText);
  1208. except
  1209. Log('Error sending installation report: ' + GetExceptionMessage);
  1210. end;
  1211. end;
  1212. end;
  1213. procedure ExecApp(Params: string; ShowCmd: Integer; Wait: TExecWait);
  1214. var
  1215. Path: string;
  1216. ErrorCode: Integer;
  1217. begin
  1218. Path := ExpandConstant('{app}\{#MainFileName}');
  1219. ExecAsOriginalUser(Path, Params, '', ShowCmd, Wait, ErrorCode)
  1220. end;
  1221. procedure OpenBrowserGettingStarted;
  1222. var
  1223. WebGettingStarted: string;
  1224. begin
  1225. WebGettingStarted :=
  1226. ExpandConstant('{#WebGettingStarted}') + PrevVersion +
  1227. '&automatic=' + IntToStr(Integer(AutomaticUpdate));
  1228. Log('Opening getting started page: ' + WebGettingStarted);
  1229. OpenBrowser(WebGettingStarted);
  1230. end;
  1231. procedure CurStepChanged(CurStep: TSetupStep);
  1232. var
  1233. ShowCmd: Integer;
  1234. OpenGettingStarted: Boolean;
  1235. UsageData: string;
  1236. CanPostInstallRuns: Boolean;
  1237. Installations: Cardinal;
  1238. begin
  1239. if CurStep = ssPostInstall then
  1240. begin
  1241. Log('Post install');
  1242. InstallationDone := True;
  1243. end
  1244. else
  1245. if CurStep = ssDone then
  1246. begin
  1247. Log('Done');
  1248. // bug in InnoSetup causes it using ssDone even when
  1249. // setup failed because machine was not restarted to complete previous
  1250. // installation. double check that ssPostInstall was called
  1251. if InstallationDone then
  1252. begin
  1253. CanPostInstallRuns := (not WizardSilent) and (not WillRestart);
  1254. OpenGettingStarted :=
  1255. OpenGettingStartedCheckbox.Enabled and
  1256. OpenGettingStartedCheckbox.Checked;
  1257. UsageData := '/Usage=';
  1258. // old style counter
  1259. UsageData := UsageData + Format('TypicalInstallation:%d,', [Integer(IsTypicalInstallation)]);
  1260. UsageData := UsageData + 'InstallationsUser+,InstallationParentProcess@,';
  1261. Installations := 0; // default, if the counter does not exist
  1262. RegQueryDWordValue(HKEY_LOCAL_MACHINE, '{#RegistryKey}', 'Installations', Installations);
  1263. Inc(Installations);
  1264. if not RegWriteDWordValue(HKEY_LOCAL_MACHINE, '{#RegistryKey}', 'Installations', Installations) then
  1265. begin
  1266. Log('Cannot increment administrator installations counter, probably a non-elevated installation');
  1267. end;
  1268. // new style counters
  1269. if not Upgrade then
  1270. begin
  1271. if IsTypicalInstallation then
  1272. UsageData := UsageData + 'InstallationsFirstTypical+,'
  1273. else
  1274. UsageData := UsageData + 'InstallationsFirstCustom+,';
  1275. end
  1276. else
  1277. begin
  1278. if AutomaticUpdate then
  1279. UsageData := UsageData + 'InstallationsUpgradeAutomatic+,'
  1280. else if IsTypicalInstallation then
  1281. UsageData := UsageData + 'InstallationsUpgradeTypical+,'
  1282. else
  1283. UsageData := UsageData + 'InstallationsUpgradeCustom+,';
  1284. end;
  1285. UsageData := UsageData + Format('LastInstallationAutomaticUpgrade:%d,', [Integer(AutomaticUpdate)]);
  1286. if (InitDir <> '') and (InitDir <> WizardForm.DirEdit.Text) then
  1287. UsageData := UsageData + 'InstallationsCustomDir+,';
  1288. if (InitComponents <> '') and (InitComponents <> SaveCheckListBoxState(WizardForm.ComponentsList)) then
  1289. UsageData := UsageData + 'InstallationsCustomComponents+,';
  1290. if (InitTasks <> '') and (InitTasks <> SaveCheckListBoxState(WizardForm.TasksList)) then
  1291. UsageData := UsageData + 'InstallationsCustomTasks+,';
  1292. if (InitInterface >= 0) and (InitInterface <> Integer(CommanderRadioButton.Checked)) then
  1293. UsageData := UsageData + 'InstallationsCustomInterface+,';
  1294. if CanPostInstallRuns and OpenGettingStarted then
  1295. UsageData := UsageData + 'InstallationsGettingStarted+,';
  1296. if CanPostInstallRuns and LaunchCheckbox.Checked then
  1297. UsageData := UsageData + 'InstallationsLaunch+,';
  1298. if WizardSilent then
  1299. UsageData := UsageData + 'InstallationsSilent+,';
  1300. if AskedRestart then
  1301. UsageData := UsageData + 'InstallationsNeedRestart+,';
  1302. if WillRestart then
  1303. UsageData := UsageData + 'InstallationsRestart+,';
  1304. if Donated then
  1305. UsageData := UsageData + 'InstallationsDonate+,';
  1306. if not IsAdminInstallMode then
  1307. UsageData := UsageData + 'InstallationsNonElevated+,';
  1308. // have to do this before running WinSCP GUI instance below,
  1309. // otherwise it loads the empty/previous counters and overwrites our changes,
  1310. // when it's closed
  1311. Log('Recording installer usage statistics: ' + UsageData);
  1312. // make sure we write the counters using the "normal" account
  1313. // (the account that will be used to report the counters)
  1314. ExecApp(UsageData, SW_HIDE, ewWaitUntilTerminated);
  1315. if AutomaticUpdate then
  1316. begin
  1317. Log('Launching WinSCP after automatic update');
  1318. ExecApp('', SW_SHOWNORMAL, ewNoWait);
  1319. if CmdLineParamExists('/OpenGettingStarted') then
  1320. begin
  1321. OpenBrowserGettingStarted;
  1322. end;
  1323. end
  1324. else
  1325. if CanPostInstallRuns then
  1326. begin
  1327. if OpenGettingStarted then
  1328. begin
  1329. OpenBrowserGettingStarted;
  1330. end;
  1331. if LaunchCheckbox.Checked then
  1332. begin
  1333. if OpenGettingStarted then
  1334. begin
  1335. Log('Will launch WinSCP minimized');
  1336. ShowCmd := SW_SHOWMINIMIZED
  1337. end
  1338. else
  1339. begin
  1340. ShowCmd := SW_SHOWNORMAL;
  1341. end;
  1342. Log('Launching WinSCP');
  1343. ExecApp('', ShowCmd, ewNoWait);
  1344. end;
  1345. end;
  1346. end;
  1347. end;
  1348. end;
  1349. #ifdef Sponsor
  1350. function CryptStringToBinary(
  1351. sz: string; cch: LongWord; flags: LongWord; binary: string; var size: LongWord;
  1352. skip: LongWord; flagsused: LongWord): Integer;
  1353. external '[email protected] stdcall';
  1354. const
  1355. CRYPT_STRING_HEX = $04;
  1356. SHCONTCH_NOPROGRESSBOX = 4;
  1357. SHCONTCH_RESPONDYESTOALL = 16;
  1358. var
  1359. ShowSponsor: Integer;
  1360. SponsoringLinkLabel, SponsorLinkLabel: TLabel;
  1361. SponsorImage: TBitmapImage;
  1362. procedure SponsorImageClick(Sender: TObject);
  1363. begin
  1364. SponsorStatus := 'C';
  1365. OpenBrowser('{#WebReport}?mode=sponsor' + Format('&sponsor=%s&', [Sponsor]) + ExpandConstant('{#WebArguments}'));
  1366. end;
  1367. function GetSponsorAreaHeight: Integer;
  1368. begin
  1369. Result := SponsorPage.SurfaceHeight - SponsorLinkLabel.Height - ScaleY(12);
  1370. end;
  1371. procedure CenterSponsorImage;
  1372. begin
  1373. if (Extended(SponsorImage.Bitmap.Width) / SponsorPage.SurfaceWidth) <
  1374. (Extended(SponsorImage.Bitmap.Height) / GetSponsorAreaHeight()) then
  1375. begin
  1376. SponsorImage.Top := 0;
  1377. SponsorImage.Height := GetSponsorAreaHeight();
  1378. SponsorImage.Width :=
  1379. Trunc((Extended(SponsorImage.Bitmap.Width) / SponsorImage.Bitmap.Height) * SponsorImage.Height);
  1380. SponsorImage.Left := (SponsorPage.SurfaceWidth - SponsorImage.Width) div 2;
  1381. end
  1382. else
  1383. begin
  1384. SponsorImage.Left := 0;
  1385. SponsorImage.Width := SponsorPage.SurfaceWidth;
  1386. SponsorImage.Height :=
  1387. Trunc((Extended(SponsorImage.Bitmap.Height) / SponsorImage.Bitmap.Width) * SponsorImage.Width);
  1388. SponsorImage.Top := (GetSponsorAreaHeight() - SponsorImage.Height) div 2;
  1389. end;
  1390. SponsorLinkLabel.Left := SponsorImage.Left;
  1391. SponsorLinkLabel.Top := GetBottom(SponsorImage) + ScaleX(6);
  1392. SponsoringLinkLabel.Left := GetRight(SponsorImage) - SponsoringLinkLabel.Width;
  1393. SponsoringLinkLabel.Top := SponsorLinkLabel.Top;
  1394. end;
  1395. procedure WizardFormResize(Sender: TObject);
  1396. begin
  1397. CenterSponsorImage;
  1398. end;
  1399. function CheckSponsorReq: Boolean;
  1400. var
  1401. R, Succeeded: Integer;
  1402. Lines: TStrings;
  1403. I, P: Integer;
  1404. L, Key, Value: string;
  1405. Stream: TStream;
  1406. Buffer: string;
  1407. Size: LongWord;
  1408. ZipPath, TargetPath, ImagePath: string;
  1409. Shell, ZipFile, TargetFolder: Variant;
  1410. SponsorArea: TBitmapImage;
  1411. ImageSize: Integer;
  1412. begin
  1413. if ShowSponsor = 0 then
  1414. begin
  1415. SponsorImage := nil;
  1416. Log('Checking for response to sponsor request');
  1417. try
  1418. Succeeded := 0;
  1419. // Not testing return value, as it always returns -1 for some reason
  1420. R := SponsorReq.WaitForResponse(1, Succeeded);
  1421. if R = 0 then
  1422. begin
  1423. Log('Timed out waiting for a response to sponsor request');
  1424. SponsorStatus := 'T';
  1425. ShowSponsor := -1;
  1426. end
  1427. else
  1428. if SponsorReq.Status <> 200 then
  1429. begin
  1430. Log('Sponsor request failed with HTTP error: ' + IntToStr(SponsorReq.Status) + ' ' + SponsorReq.StatusText);
  1431. SponsorStatus := 'H';
  1432. ShowSponsor := -1;
  1433. end
  1434. else
  1435. begin
  1436. Log('Sponsor request succeeded');
  1437. if CmdLineParamExists('/SponsorArea') then
  1438. begin
  1439. SponsorArea := TBitmapImage.Create(SponsorPage);
  1440. SponsorArea.Parent := SponsorPage.Surface;
  1441. SponsorArea.Visible := CmdLineParamExists('/SponsorArea');
  1442. SponsorArea.BackColor := clTeal;
  1443. SponsorArea.Anchors := [akLeft, akTop, akRight, akBottom];
  1444. end
  1445. else
  1446. begin
  1447. SponsorArea := nil;
  1448. end;
  1449. SponsorLinkLabel := TLabel.Create(SponsorPage);
  1450. SponsorLinkLabel.Parent := SponsorPage.Surface;
  1451. SponsorLinkLabel.Caption := 'Visit release sponsor';
  1452. SponsorLinkLabel.OnClick := @SponsorImageClick;
  1453. LinkLabel(SponsorLinkLabel);
  1454. SponsoringLinkLabel := TLabel.Create(SponsorPage);
  1455. SponsoringLinkLabel.Parent := SponsorPage.Surface;
  1456. SponsoringLinkLabel.Caption := 'Become next release sponsor';
  1457. SponsoringLinkLabel.OnClick := @SponsoringLinkLabelClick;
  1458. LinkLabel(SponsoringLinkLabel);
  1459. Lines := TStringList.Create;
  1460. try
  1461. Lines.Text := SponsorReq.ResponseText;
  1462. for I := 0 to Lines.Count - 1 do
  1463. begin
  1464. L := Lines[I];
  1465. P := Pos('=', L);
  1466. if P = 0 then
  1467. begin
  1468. Log('Malformed sponsor response directive: ' + L);
  1469. SponsorStatus := 'P';
  1470. ShowSponsor := -1;
  1471. break;
  1472. end
  1473. else
  1474. begin
  1475. Key := Trim(Copy(L, 1, P - 1));
  1476. Value := Trim(Copy(L, P + 1, Length(L) - P));
  1477. if CompareText(Key, 'result') = 0 then
  1478. begin
  1479. if Value <> '-' then
  1480. begin
  1481. Log('No sponsor returned');
  1482. SponsorStatus := Copy(Value, 1, 1);
  1483. ShowSponsor := -1;
  1484. break;
  1485. end
  1486. else
  1487. begin
  1488. Log('Sponsor returned');
  1489. end;
  1490. end
  1491. else
  1492. if (CompareText(Key, 'image') = 0) or
  1493. (CompareText(Key, 'localimage') = 0) then
  1494. begin
  1495. if CompareText(Key, 'localimage') = 0 then
  1496. begin
  1497. Log(Format('Extracting embedded sponsor image (%s)', [Value]));
  1498. ExtractTemporaryFile(Value);
  1499. ImagePath := ExpandConstant('{tmp}\' + Value);
  1500. end
  1501. else
  1502. begin
  1503. Log(Format('Extracting returned sponsor image (%d bytes)', [Length(Value)]));
  1504. ZipPath := ExpandConstant('{tmp}\sponsor.zip');
  1505. Stream := TFileStream.Create(ZipPath, fmCreate);
  1506. try
  1507. SetLength(Buffer, (Length(Value) div 4) + 1);
  1508. Size := Length(Value) div 2;
  1509. if (CryptStringToBinary(Value, Length(Value), CRYPT_STRING_HEX, Buffer, Size, 0, 0) = 0) or
  1510. (Size <> Length(Value) div 2) then
  1511. begin
  1512. Log('Error decoding binary string');
  1513. SponsorStatus := 'P';
  1514. ShowSponsor := -1;
  1515. break;
  1516. end;
  1517. Stream.WriteBuffer(Buffer, Size);
  1518. finally
  1519. Stream.Free;
  1520. end;
  1521. Shell := CreateOleObject('Shell.Application');
  1522. ZipFile := Shell.NameSpace(ZipPath);
  1523. if VarIsClear(ZipFile) then
  1524. begin
  1525. RaiseException(Format('ZIP file "%s" does not exist or cannot be opened', [ZipPath]));
  1526. end
  1527. else
  1528. begin
  1529. TargetPath := ExpandConstant('{tmp}');
  1530. TargetFolder := Shell.NameSpace(TargetPath);
  1531. if VarIsClear(TargetFolder) then
  1532. begin
  1533. RaiseException(Format('Target path "%s" does not exist', [TargetPath]));
  1534. end
  1535. else
  1536. begin
  1537. TargetFolder.CopyHere(ZipFile.Items, SHCONTCH_NOPROGRESSBOX or SHCONTCH_RESPONDYESTOALL);
  1538. ImagePath := ExpandConstant('{tmp}\sponsor.bmp');
  1539. end;
  1540. end;
  1541. end;
  1542. SponsorImage := TBitmapImage.Create(SponsorPage);
  1543. SponsorImage.Parent := SponsorPage.Surface;
  1544. SponsorImage.Hint := SponsorLinkLabel.Caption;
  1545. SponsorImage.ShowHint := True;
  1546. SponsorImage.Stretch := True;
  1547. try
  1548. LoadBitmap(SponsorImage, ImagePath);
  1549. except
  1550. Log('Error loading sponsor image: ' + GetExceptionMessage);
  1551. SponsorStatus := 'I';
  1552. ShowSponsor := -1;
  1553. end;
  1554. if ShowSponsor = 0 then
  1555. begin
  1556. if Assigned(SponsorArea) then
  1557. begin
  1558. SponsorArea.Left := 0;
  1559. SponsorArea.Top := 0;
  1560. SponsorArea.Width := SponsorPage.Surface.Width;
  1561. SponsorArea.Height := GetSponsorAreaHeight();
  1562. Log(Format('Sponsor area is %dx%d', [SponsorArea.Width, SponsorArea.Height]));
  1563. end;
  1564. CenterSponsorImage;
  1565. SponsorImage.Cursor := crHand;
  1566. SponsorImage.OnClick := @SponsorImageClick;
  1567. WizardForm.OnResize := @WizardFormResize;
  1568. FileSize(ImagePath, ImageSize);
  1569. Log(Format('Sponsor image loaded (%d bytes, %dx%d) and displayed (%dx%d)', [
  1570. Integer(ImageSize), SponsorImage.Bitmap.Width, SponsorImage.Bitmap.Height,
  1571. SponsorImage.Width, SponsorImage.Height]));
  1572. end;
  1573. end
  1574. else
  1575. if CompareText(Key, 'sponsor') = 0 then
  1576. begin
  1577. Sponsor := Value;
  1578. end
  1579. else
  1580. if CompareText(Key, 'description') = 0 then
  1581. begin
  1582. SponsorPage.Description := Value;
  1583. Log('Sponsor page description: ' + Value);
  1584. end
  1585. else
  1586. if CompareText(Key, 'caption') = 0 then
  1587. begin
  1588. SponsorPage.Caption := Value;
  1589. Log('Sponsor page caption: ' + Value);
  1590. end
  1591. else
  1592. if CompareText(Key, 'sponsor_caption') = 0 then
  1593. begin
  1594. SponsorLinkLabel.Caption := Value;
  1595. Log('Sponsor link caption: ' + Value);
  1596. end
  1597. else
  1598. if CompareText(Key, 'sponsoring_caption') = 0 then
  1599. begin
  1600. if Value = '' then
  1601. begin
  1602. SponsoringLinkLabel.Visible := False;
  1603. Log('Hiding sponsoring link');
  1604. end
  1605. else
  1606. begin
  1607. SponsoringLinkLabel.Caption := Value;
  1608. Log('Sponsoring link caption: ' + Value);
  1609. end;
  1610. end
  1611. else
  1612. begin
  1613. Log('Unknown sponsor directive: ' + Key);
  1614. end;
  1615. end;
  1616. end;
  1617. finally
  1618. Lines.Free;
  1619. end;
  1620. if ShowSponsor = 0 then
  1621. begin
  1622. if SponsorImage = nil then
  1623. begin
  1624. Log('Incomplete sponsor data');
  1625. SponsorStatus := 'P';
  1626. ShowSponsor := -1;
  1627. end
  1628. else
  1629. begin
  1630. SponsorStatus := 'S';
  1631. ShowSponsor := 1;
  1632. end;
  1633. end;
  1634. end;
  1635. except
  1636. Log('Error processing response to sponsor request: ' + GetExceptionMessage);
  1637. SponsorStatus := 'E';
  1638. ShowSponsor := -1;
  1639. end;
  1640. end;
  1641. Result := (ShowSponsor > 0);
  1642. end;
  1643. #endif
  1644. function ShouldSkipPage(PageID: Integer): Boolean;
  1645. begin
  1646. Result :=
  1647. { Hide most pages during typical installation }
  1648. (IsTypicalInstallation and
  1649. ((PageID = wpSelectDir) or (PageID = wpSelectComponents) or
  1650. (PageID = wpSelectTasks) or
  1651. { Hide Interface page for upgrades only, show for fresh installs }
  1652. ((PageID = InterfacePage.ID) and Upgrade)))
  1653. #ifdef Sponsor
  1654. or
  1655. ((SponsorPage <> nil) and (PageID = SponsorPage.ID) and (not CheckSponsorReq))
  1656. #endif
  1657. ;
  1658. end;
  1659. function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo,
  1660. MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: string): string;
  1661. var
  1662. S: string;
  1663. S2: string;
  1664. begin
  1665. S := '';
  1666. S := S + MemoDirInfo + NewLine + NewLine;
  1667. if not Upgrade then
  1668. begin
  1669. if IsTypicalInstallation then S2 := CustomMessage('TypicalType')
  1670. else S2 := CustomMessage('CustomType');
  1671. end
  1672. else
  1673. begin
  1674. if IsTypicalInstallation then S2 := CustomMessage('TypicalUpgradeType')
  1675. else S2 := CustomMessage('CustomUpgradeType');
  1676. end;
  1677. StringChange(S2, '&', '');
  1678. S := S + SetupMessage(msgReadyMemoType) + NewLine + Space + S2 + NewLine + NewLine;
  1679. S := S + MemoComponentsInfo + NewLine + NewLine;
  1680. if Length(MemoGroupInfo) > 0 then
  1681. S := S + MemoGroupInfo + NewLine + NewLine;
  1682. if Length(MemoTasksInfo) > 0 then
  1683. S := S + MemoTasksInfo + NewLine + NewLine;
  1684. S := S + CustomMessage('UserSettingsOverview') + NewLine;
  1685. S := S + Space;
  1686. if CommanderRadioButton.Checked then S2 := CustomMessage('NortonCommanderInterfaceC')
  1687. else S2 := CustomMessage('ExplorerInterfaceC');
  1688. StringChange(S2, '&', '');
  1689. S := S + S2;
  1690. S := S + NewLine;
  1691. Result := S;
  1692. end;
  1693. function InitializeUninstall: Boolean;
  1694. begin
  1695. // let application know that we are running silent uninstall,
  1696. // this turns UninstallCleanup to noop
  1697. if UninstallSilent then
  1698. CreateMutex('WinSCPSilentUninstall');
  1699. Result := True;
  1700. end;
  1701. function HasUserPrograms: Boolean;
  1702. begin
  1703. // To avoid the installer failing when the {userprograms}
  1704. // cannot be resolved (when installing via system account or SCCM)
  1705. try
  1706. ExpandConstant('{userprograms}');
  1707. Log('Have user programs');
  1708. Result := True;
  1709. except
  1710. Log('Does not have user programs');
  1711. Result := False;
  1712. end;
  1713. end;
  1714. #expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")