diff --git a/browser/branding/official/branding.nsi b/browser/branding/official/branding.nsi index 684db0ffe9d..a817e0391c7 100644 --- a/browser/branding/official/branding.nsi +++ b/browser/branding/official/branding.nsi @@ -13,9 +13,10 @@ !define URLInfoAbout "http://www.mozilla.com/${AB_CD}/" !define URLUpdateInfo "http://www.mozilla.com/${AB_CD}/firefox/" -; Prevents the beta channel urls in stub.nsi from being used when not using -; official branding -!define Official +; The OFFICIAL define is a workaround to support different urls for Release and +; Beta since they share the same branding when building with other branches that +; set the update channel to beta. +!define OFFICIAL !define URLStubDownload "http://download.mozilla.org/?product=firefox-latest&os=win&lang=${AB_CD}" !define URLManualDownload "https://www.mozilla.org/firefox/installer-help/?channel=release" !define Channel "release" diff --git a/browser/installer/windows/nsis/defines.nsi.in b/browser/installer/windows/nsis/defines.nsi.in index 54367764ffb..a976395555a 100644 --- a/browser/installer/windows/nsis/defines.nsi.in +++ b/browser/installer/windows/nsis/defines.nsi.in @@ -29,13 +29,19 @@ # The value below removes all LSP categories previously set. !define LSP_CATEGORIES "0x00000000" +!if "@MOZ_UPDATE_CHANNEL@" == "" +!define UpdateChannel "Unknown" +!else +!define UpdateChannel "@MOZ_UPDATE_CHANNEL@" +!endif + # Due to official and beta using the same branding this is needed to # differentiante between the url used by the stub for downloading. !if "@MOZ_UPDATE_CHANNEL@" == "beta" !define BETA_UPDATE_CHANNEL !endif -!define BaseURLStubPing "http://download-stats.mozilla.org/stub/v4/" +!define BaseURLStubPing "http://download-stats.mozilla.org/stub" # NO_INSTDIR_FROM_REG is defined for pre-releases which have a PreReleaseSuffix # (e.g. Alpha X, Beta X, etc.) to prevent finding a non-default installation diff --git a/browser/installer/windows/nsis/stub.nsi b/browser/installer/windows/nsis/stub.nsi index 465c339988a..738206c989c 100644 --- a/browser/installer/windows/nsis/stub.nsi +++ b/browser/installer/windows/nsis/stub.nsi @@ -56,7 +56,7 @@ Var CanWriteToInstallDir Var HasRequiredSpaceAvailable Var IsDownloadFinished Var Initialized -Var DownloadSize +Var DownloadSizeBytes Var HalfOfDownload Var DownloadReset Var ExistingTopDir @@ -64,33 +64,111 @@ Var SpaceAvailableBytes Var InitialInstallDir Var HandleDownload Var CanSetAsDefault -Var TmpVal Var InstallCounterStep +Var TmpVal Var ExitCode -Var StartTickCount -Var DownloadTickCount -Var InstallTickCount -Var FinishTickCount +Var FirefoxLaunchCode + +; The first three tick counts are for the start of a phase and equate equate to +; the display of individual installer pages. +Var StartIntroPhaseTickCount +Var StartOptionsPhaseTickCount +Var StartDownloadPhaseTickCount +; Since the Intro and Options pages can be displayed multiple times the total +; seconds spent on each of these pages is reported. +Var IntroPhaseSeconds +Var OptionsPhaseSeconds +; The tick count for the last download +Var StartLastDownloadTickCount +; The number of seconds from the start of the download phase until the first +; bytes are received. This is only recorded for first request so it is possible +; to determine connection issues for the first request. +Var DownloadFirstTransferSeconds +; The last four tick counts are for the end of a phase in the installation page. +; the options phase when it isn't entered. +Var EndDownloadPhaseTickCount +Var EndPreInstallPhaseTickCount +Var EndInstallPhaseTickCount +Var EndFinishPhaseTickCount + +Var IntroPageShownCount +Var OptionsPageShownCount +Var InitialInstallRequirementsCode Var ExistingProfile -Var ExistingInstall -Var DownloadedAmount -Var FirefoxLaunch +Var ExistingVersion +Var ExistingBuildID +Var DownloadedBytes +Var DownloadRetryCount +Var OpenedDownloadPage +Var DownloadServerIP -Var HEIGHT_PX -Var CTL_RIGHT_PX +Var ControlHeightPX +Var ControlRightPX +; Uncomment the following to prevent pinging the metrics server when testing +; the stub installer +;!define STUB_DEBUG + +!define StubURLVersion "v5" + +; Successful install exit code !define ERR_SUCCESS 0 -!define ERR_CANCEL_DOWNLOAD 10 -!define ERR_INVALID_HANDLE 11 -!define ERR_CERT_UNTRUSTED 12 -!define ERR_CERT_ATTRIBUTES 13 -!define ERR_CERT_UNTRUSTED_AND_ATTRIBUTES 14 -!define ERR_CHECK_INSTALL_TIMEOUT 15 -!define ERR_UNKNOWN 99 -!define DownloadIntervalMS 200 ; Interval for the download timer -!define InstallIntervalMS 100 ; Interval for the install timer +/** + * The following errors prefixed with ERR_DOWNLOAD apply to the download phase. + */ +; The download was cancelled by the user +!define ERR_DOWNLOAD_CANCEL 10 + +; Too many attempts to download. The maximum attempts is defined in +; DownloadMaxRetries. +!define ERR_DOWNLOAD_TOO_MANY_RETRIES 11 + +/** + * The following errors prefixed with ERR_PREINSTALL apply to the pre-install + * check phase. + */ +; Unable to acquire a file handle to the downloaded file +!define ERR_PREINSTALL_INVALID_HANDLE 20 + +; The downloaded file's certificate is not trusted by the certificate store. +!define ERR_PREINSTALL_CERT_UNTRUSTED 21 + +; The downloaded file's certificate attribute values were incorrect. +!define ERR_PREINSTALL_CERT_ATTRIBUTES 22 + +; The downloaded file's certificate is not trusted by the certificate store and +; certificate attribute values were incorrect. +!define ERR_PREINSTALL_CERT_UNTRUSTED_AND_ATTRIBUTES 23 + +/** + * The following errors prefixed with ERR_INSTALL apply to the install phase. + */ +; The installation timed out. The installation timeout is defined by the number +; of progress steps defined in InstallProgresSteps and the install timer +; interval defined in InstallIntervalMS +!define ERR_INSTALL_TIMEOUT 30 + +; Maximum times to retry the download before displaying an error +!define DownloadMaxRetries 9 + +; Minimum size expected to download in bytes +!define DownloadMinSizeBytes 15728640 ; 15 MB + +; Maximum size expected to download in bytes +!define DownloadMaxSizeBytes 36700160 ; 35 MB + +; Interval before retrying to download. 3 seconds is used along with 10 +; attempted downloads (the first attempt along with 9 retries) to give a +; minimum of 30 seconds or retrying before giving up. +!define DownloadRetryIntervalMS 3000 + +; Interval for the download timer +!define DownloadIntervalMS 200 + +; Interval for the install timer +!define InstallIntervalMS 100 ; Number of steps for the install progress. ; This is 120 seconds with a 100 millisecond timer and a first step of 20 as @@ -99,10 +177,14 @@ Var CTL_RIGHT_PX ; if it reaches this number. The size of the install progress step increases ; when the full installer finishes instead of waiting the entire 120 seconds. !define InstallProgresSteps 1220 + ; The first step for the install progress bar. By starting with a large step ; immediate feedback is given to the user. !define InstallProgressFirstStep 20 +; The interval in MS used for the progress bars set as marquee. +!define ProgressbarMarqueeIntervalMS 10 + ; On Vista and above attempt to elevate Standard Users in addition to users that ; are a member of the Administrators group. !define NONADMIN_ELEVATE @@ -114,10 +196,7 @@ Var CTL_RIGHT_PX !define FILE_SHARE_READ 1 !define GENERIC_READ 0x80000000 !define OPEN_EXISTING 3 -!define FILE_BEGIN 0 -!define FILE_END 2 !define INVALID_HANDLE_VALUE -1 -!define INVALID_FILE_SIZE 0xffffffff !include "nsDialogs.nsh" !include "LogicLib.nsh" @@ -134,9 +213,10 @@ Var CTL_RIGHT_PX !include "defines.nsi" -; Workaround to support different urls for Official and Beta since they share -; the same branding. -!ifdef Official +; The OFFICIAL define is a workaround to support different urls for Release and +; Beta since they share the same branding when building with other branches that +; set the update channel to beta. +!ifdef OFFICIAL !ifdef BETA_UPDATE_CHANNEL !undef URLStubDownload !define URLStubDownload "http://download.mozilla.org/?product=firefox-beta-latest&os=win&lang=${AB_CD}" @@ -202,7 +282,7 @@ Caption "$(WIN_CAPTION)" Page custom createDummy ; Needed to enable the Intro page's back button Page custom createIntro leaveIntro ; Introduction page Page custom createOptions leaveOptions ; Options page -Page custom createInstall leaveInstall ; Download / Installation page +Page custom createInstall ; Download / Installation page Function .onInit ; Remove the current exe directory from the search order. @@ -240,7 +320,7 @@ Function .onInit ; and possibly the IsWinXP test as well. To work around this also ; check if the Windows NT registry Key exists and if it does if the ; first char in CurrentVersion is equal to 3 (Windows NT 3.5 and - ; 3.5.1), to 4 (Windows NT 4) or 5 (Windows 2000 and Windows XP). + ; 3.5.1), 4 (Windows NT 4), or 5 (Windows 2000 and Windows XP). StrCpy $R8 "" ClearErrors ReadRegStr $R8 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentVersion" @@ -318,9 +398,17 @@ Function .onInit StrCpy $CanSetAsDefault "true" ${EndIf} + ; Initialize the majority of variables except those that need to be reset + ; when a page is displayed. + StrCpy $IntroPhaseSeconds "0" + StrCpy $OptionsPhaseSeconds "0" + StrCpy $EndPreInstallPhaseTickCount "0" + StrCpy $EndInstallPhaseTickCount "0" + StrCpy $IntroPageShownCount "0" + StrCpy $OptionsPageShownCount "0" + StrCpy $InitialInstallRequirementsCode "" StrCpy $IsDownloadFinished "" - StrCpy $FirefoxLaunch "0" - StrCpy $ExitCode "${ERR_UNKNOWN}" + StrCpy $FirefoxLaunchCode "0" CreateFont $FontBlurb "$(^Font)" "12" "500" CreateFont $FontNormal "$(^Font)" "11" "500" @@ -352,45 +440,6 @@ FunctionEnd !endif Function .onGUIEnd - ; Try to send a ping if a download was attempted - ${If} $IsDownloadFinished != "" - ${AndIf} $CheckboxSendPing == 1 - ${If} $IsDownloadFinished == "false" - ; When the value of $IsDownloadFinished is false the download was started - ; but didn't finish and GetTickCount needs to be called to determine how - ; long the download was in progress. - System::Call "kernel32::GetTickCount()l .s" - Pop $DownloadTickCount - StrCpy $1 "0" - StrCpy $2 "0" - - ; Cancel the download in progress - InetBgDL::Get /RESET /END - ${Else} - ; Get the tick count for when the installer closes. - System::Call "kernel32::GetTickCount()l .s" - Pop $FinishTickCount - ; Get the time from the end of the install to close the installer. - ${GetSecondsElapsed} "$InstallTickCount" "$FinishTickCount" $2 - - ; Get the time from the end of the download to the completion of the - ; installation. - ${GetSecondsElapsed} "$DownloadTickCount" "$InstallTickCount" $1 - ${EndIf} - - ; Get the time from the start of the download to the end of the download. - ${GetSecondsElapsed} "$StartTickCount" "$DownloadTickCount" $0 - - System::Int64Op $DownloadedAmount / 1024 - Pop $DownloadedAmount - - InetBgDL::Get "${BaseURLStubPing}${Channel}/${AB_CD}/$ExitCode/$FirefoxLaunch/$DownloadedAmount/$0/$1/$2/$ExistingProfile/$ExistingInstall/" \ - "$PLUGINSDIR\_temp" /END - ${ElseIf} $IsDownloadFinished == "false" - ; Cancel the download in progress - InetBgDL::Get /RESET /END - ${EndIf} - ${UnloadUAC} FunctionEnd @@ -405,6 +454,183 @@ Function .onUserAbort Delete "$PLUGINSDIR\_temp" Delete "$PLUGINSDIR\download.exe" Delete "$PLUGINSDIR\${CONFIG_INI}" + ${If} "$IsDownloadFinished" == "" + ${OrIf} $CheckboxSendPing != 1 + ; When not sending a ping cancel the download if it is in progress and exit + ; the installer. + ${If} "$IsDownloadFinished" == "false" + HideWindow + InetBgDL::Get /RESET /END + ${EndIf} + ${Else} + Call SendPing + ; Aborting the abort will allow SendPing to hide the installer window and + ; close the installer after it sends the metrics ping. + Abort + ${EndIf} +FunctionEnd + +Function SendPing + HideWindow + ; Try to send a ping if a download was attempted + ${If} $CheckboxSendPing == 1 + ${AndIf} $IsDownloadFinished != "" + ; Get the tick count for the completion of all phases. + System::Call "kernel32::GetTickCount()l .s" + Pop $EndFinishPhaseTickCount + + ; When the value of $IsDownloadFinished is false the download was started + ; but didn't finish. In this case the tick count stored in + ; $EndFinishPhaseTickCount is used to determine how long the download was + ; in progress. + ${If} "$IsDownloadFinished" == "false" + StrCpy $EndDownloadPhaseTickCount "$EndFinishPhaseTickCount" + ; Cancel the download in progress + InetBgDL::Get /RESET /END + ${EndIf} + + + ; When $DownloadFirstTransferSeconds equals an empty string the download + ; never successfully started so set the value to 0. It will be possible to + ; determine that the download didn't successfully start from the seconds for + ; the last download. + ${If} "$DownloadFirstTransferSeconds" == "" + StrCpy $DownloadFirstTransferSeconds "0" + ${EndIf} + + ; When $StartLastDownloadTickCount equals 0 the download never successfully + ; started so set the value to $EndDownloadPhaseTickCount to compute the + ; correct value. + ${If} $StartLastDownloadTickCount == "0" + ; This could happen if the download never successfully starts + StrCpy $StartLastDownloadTickCount "$EndDownloadPhaseTickCount" + ${EndIf} + + ; When $EndPreInstallPhaseTickCount equals 0 the installation phase was + ; never completed so set its value to $EndFinishPhaseTickCount to compute + ; the correct value. + ${If} "$EndPreInstallPhaseTickCount" == "0" + StrCpy $EndPreInstallPhaseTickCount "$EndFinishPhaseTickCount" + ${EndIf} + + ; When $EndInstallPhaseTickCount equals 0 the installation phase was never + ; completed so set its value to $EndFinishPhaseTickCount to compute the + ; correct value. + ${If} "$EndInstallPhaseTickCount" == "0" + StrCpy $EndInstallPhaseTickCount "$EndFinishPhaseTickCount" + ${EndIf} + + ; Get the seconds elapsed from the start of the download phase to the end of + ; the download phase. + ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$EndDownloadPhaseTickCount" $0 + + ; Get the seconds elapsed from the start of the last download to the end of + ; the last download. + ${GetSecondsElapsed} "$StartLastDownloadTickCount" "$EndDownloadPhaseTickCount" $1 + + ; Get the seconds elapsed from the end of the download phase to the + ; completion of the pre-installation check phase. + ${GetSecondsElapsed} "$EndDownloadPhaseTickCount" "$EndPreInstallPhaseTickCount" $2 + + ; Get the seconds elapsed from the end of the pre-installation check phase + ; to the completion of the installation phase. + ${GetSecondsElapsed} "$EndPreInstallPhaseTickCount" "$EndInstallPhaseTickCount" $3 + + ; Get the seconds elapsed from the end of the installation phase to the + ; completion of all phases. + ${GetSecondsElapsed} "$EndInstallPhaseTickCount" "$EndFinishPhaseTickCount" $4 + +!ifdef HAVE_64BIT_OS + StrCpy $R0 "1" +!else + StrCpy $R0 "0" +!endif + + ${If} ${RunningX64} + StrCpy $R1 "1" + ${Else} + StrCpy $R1 "0" + ${EndIf} + + ${WinVerGetMajor} $R2 + ${WinVerGetMinor} $R3 + ${WinVerGetBuild} $R4 + + ${If} "$ExitCode" == "${ERR_SUCCESS}" + ReadINIStr $R5 "$INSTDIR\application.ini" "App" "Version" + ReadINIStr $R6 "$INSTDIR\application.ini" "App" "BuildID" + ${Else} + StrCpy $R5 "0" + StrCpy $R6 "0" + ${EndIf} + + ; Whether installed into the default installation directory + ${GetLongPath} "$INSTDIR" $R7 + ${GetLongPath} "$InitialInstallDir" $R8 + ${If} "$R7" == "$R8" + StrCpy $R7 "1" + ${Else} + StrCpy $R7 "0" + ${EndIf} + + ClearErrors + WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \ + "Write Test" + ${If} ${Errors} + StrCpy $R8 "0" + ${Else} + DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" + StrCpy $R8 "1" + ${EndIf} + + ${If} "$DownloadServerIP" == "" + StrCpy $DownloadServerIP "Unknown" + ${EndIf} + +!ifdef STUB_DEBUG + MessageBox MB_OK "${BaseURLStubPing} \ + $\nStub URL Version = ${StubURLVersion} \ + $\nBuild Channel = ${Channel} \ + $\nUpdate Channel = ${UpdateChannel} \ + $\nLocale = ${AB_CD} \ + $\nFirefox x64 = $R0 \ + $\nRunning x64 Windows = $R1 \ + $\nMajor = $R2 \ + $\nMinor = $R3 \ + $\nBuild = $R4 \ + $\nExit Code = $ExitCode \ + $\nFirefox Launch Code = $FirefoxLaunchCode \ + $\nDownload Retry Count = $DownloadRetryCount \ + $\nDownloaded Bytes = $DownloadedBytes \ + $\nIntroduction Phase Seconds = $IntroPhaseSeconds \ + $\nOptions Phase Seconds = $OptionsPhaseSeconds \ + $\nDownload Phase Seconds = $0 \ + $\nDownload First Transfer Seconds = $DownloadFirstTransferSeconds \ + $\nLast Download Seconds = $1 \ + $\nPreinstall Phase Seconds = $2 \ + $\nInstall Phase Seconds = $3 \ + $\nFinish Phase Seconds = $4 \ + $\nIntro Page Shown Count = $IntroPageShownCount \ + $\nOptions Page Shown Count = $OptionsPageShownCount \ + $\nInitial Install Requirements Code = $InitialInstallRequirementsCode \ + $\nOpened Download Page = $OpenedDownloadPage \ + $\nExisting Profile = $ExistingProfile \ + $\nExisting Version = $ExistingVersion \ + $\nExisting Build ID = $ExistingBuildID \ + $\nNew Version = $R5 \ + $\nNew Build ID = $R6 \ + $\nDefault Install Dir = $R7 \ + $\nHas Admin = $R8 \ + $\nDownload Server IP = $DownloadServerIP" +!else + ${NSD_CreateTimer} OnPing ${DownloadIntervalMS} + InetBgDL::Get "${BaseURLStubPing}/${StubURLVersion}/${Channel}/${UpdateChannel}/${AB_CD}/$R0/$R1/$R2/$R3/$R4/$ExitCode/$FirefoxLaunchCode/$DownloadRetryCount/$DownloadedBytes/$IntroPhaseSeconds/$OptionsPhaseSeconds/$0/$1/$DownloadFirstTransferSeconds/$2/$3/$4/$IntroPageShownCount/$OptionsPageShownCount/$InitialInstallRequirementsCode/$OpenedDownloadPage/$ExistingProfile/$ExistingVersion/$ExistingBuildID/$R5/$R6/$R7/$R8/$DownloadServerIP" \ + "$PLUGINSDIR\_temp" /END +!endif + ${ElseIf} "$IsDownloadFinished" == "false" + ; Cancel the download in progress + InetBgDL::Get /RESET /END + ${EndIf} FunctionEnd Function createDummy @@ -422,12 +648,11 @@ Function createIntro !else StrCpy $CheckboxInstallMaintSvc "0" !endif + StrCpy $WasOptionsButtonClicked "0" nsDialogs::Create /NOUNLOAD 1018 Pop $Dialog - StrCpy $WasOptionsButtonClicked "" - GetFunctionAddress $0 OnBack nsDialogs::OnBack /NOUNLOAD $0 @@ -443,12 +668,20 @@ Function createIntro SendMessage $0 ${WM_SETFONT} $FontBlurb 0 SetCtlColors $0 ${INTRO_BLURB_TEXT_COLOR} transparent - ${Unless} $Initialized == "true" + ${If} "$Initialized" == "true" + ; When the user clicked back from the options page. + System::Call "kernel32::GetTickCount()l .s" + Pop $0 + ${GetSecondsElapsed} "$StartOptionsPhaseTickCount" "$0" $1 + ; This is added to the previous value of $OptionsPhaseSeconds because the + ; options page can be displayed multiple times. + IntOp $OptionsPhaseSeconds $OptionsPhaseSeconds + $1 + ${Else} SetCtlColors $HWNDPARENT ${FOOTER_CONTROL_TEXT_COLOR_NORMAL} ${FOOTER_BKGRD_COLOR} GetDlgItem $0 $HWNDPARENT 10 ; Default browser checkbox ; Set as default is not supported in the installer for Win8 and above so ; only display it on Windows 7 and below - ${If} $CanSetAsDefault == "true" + ${If} "$CanSetAsDefault" == "true" ; The uxtheme must be disabled on checkboxes in order to override the ; system font color. System::Call 'uxtheme::SetWindowTheme(i $0 , w " ", w " ")' @@ -461,7 +694,7 @@ Function createIntro ${EndIf} GetDlgItem $0 $HWNDPARENT 11 ShowWindow $0 ${SW_HIDE} - ${EndUnless} + ${EndIf} ${NSD_CreateBitmap} ${APPNAME_BMP_EDGE_DU} ${APPNAME_BMP_TOP_DU} \ ${APPNAME_BMP_WIDTH_DU} ${APPNAME_BMP_HEIGHT_DU} "" @@ -482,6 +715,11 @@ Function createIntro GetDlgItem $0 $HWNDPARENT 3 ; Back and Options button SendMessage $0 ${WM_SETTEXT} 0 "STR:$(OPTIONS_BUTTON)" + IntOp $IntroPageShownCount $IntroPageShownCount + 1 + + System::Call "kernel32::GetTickCount()l .s" + Pop $StartIntroPhaseTickCount + LockWindow off nsDialogs::Show @@ -493,6 +731,14 @@ FunctionEnd Function leaveIntro LockWindow on + + System::Call "kernel32::GetTickCount()l .s" + Pop $0 + ${GetSecondsElapsed} "$StartIntroPhaseTickCount" "$0" $1 + ; This is added to the previous value of $IntroPhaseSeconds because the + ; introduction page can be displayed multiple times. + IntOp $IntroPhaseSeconds $IntroPhaseSeconds + $1 + SetShellVarContext all ; Set SHCTX to All Users ; If the user doesn't have write access to the installation directory set ; the installation directory to a subdirectory of the All Users application @@ -522,11 +768,28 @@ Function leaveIntro FunctionEnd Function createOptions - ; Skip the options page unless the Options button was clicked - ${If} "$WasOptionsButtonClicked" != "true" + ; Check whether the requirements to install are satisfied the first time the + ; options page is displayed for metrics. + ${If} "$InitialInstallRequirementsCode" == "" + ${If} "$CanWriteToInstallDir" != "true" + ${AndIf} "$HasRequiredSpaceAvailable" != "true" + StrCpy $InitialInstallRequirementsCode "1" + ${ElseIf} "$CanWriteToInstallDir" != "true" + StrCpy $InitialInstallRequirementsCode "2" + ${ElseIf} "$HasRequiredSpaceAvailable" != "true" + StrCpy $InitialInstallRequirementsCode "3" + ${Else} + StrCpy $InitialInstallRequirementsCode "0" + ${EndIf} + ${EndIf} + + ; Skip the options page unless the Options button was clicked as long as the + ; installation directory can be written to and there is the minimum required + ; space available. + ${If} "$WasOptionsButtonClicked" != "1" ${If} "$CanWriteToInstallDir" == "true" ${AndIf} "$HasRequiredSpaceAvailable" == "true" - Abort + Abort ; Skip the options page ${EndIf} ${EndIf} @@ -615,20 +878,20 @@ Function createOptions ${GetTextExtent} "$(SPACE_REQUIRED)" $FontItalic $0 $1 ${GetTextExtent} "$(SPACE_AVAILABLE)" $FontItalic $2 $3 ${If} $1 > $3 - StrCpy $HEIGHT_PX "$1" + StrCpy $ControlHeightPX "$1" ${Else} - StrCpy $HEIGHT_PX "$3" + StrCpy $ControlHeightPX "$3" ${EndIf} IntOp $0 $0 + 8 ; Add padding to the control's width ; Make both controls the same width as the widest control - ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 134u $0 $HEIGHT_PX "$(SPACE_REQUIRED)" + ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 134u $0 $ControlHeightPX "$(SPACE_REQUIRED)" Pop $5 SetCtlColors $5 ${OPTIONS_TEXT_COLOR_FADED} ${OPTIONS_BKGRD_COLOR} SendMessage $5 ${WM_SETFONT} $FontItalic 0 IntOp $2 $2 + 8 ; Add padding to the control's width - ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 145u $2 $HEIGHT_PX "$(SPACE_AVAILABLE)" + ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 145u $2 $ControlHeightPX "$(SPACE_AVAILABLE)" Pop $6 SetCtlColors $6 ${OPTIONS_TEXT_COLOR_FADED} ${OPTIONS_BKGRD_COLOR} SendMessage $6 ${WM_SETFONT} $FontItalic 0 @@ -638,11 +901,11 @@ Function createOptions StrCpy $6 "$5" ${EndIf} FindWindow $1 "#32770" "" $HWNDPARENT - ${GetDlgItemEndPX} $6 $CTL_RIGHT_PX + ${GetDlgItemEndPX} $6 $ControlRightPX - IntOp $CTL_RIGHT_PX $CTL_RIGHT_PX + 6 + IntOp $ControlRightPX $ControlRightPX + 6 - ${NSD_CreateLabel} $CTL_RIGHT_PX 134u 100% $HEIGHT_PX \ + ${NSD_CreateLabel} $ControlRightPX 134u 100% $ControlHeightPX \ "${APPROXIMATE_REQUIRED_SPACE_MB} $(MEGA)$(BYTE)" Pop $7 SetCtlColors $7 ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR} @@ -650,7 +913,7 @@ Function createOptions ; Create the free space label with an empty string and update it by calling ; UpdateFreeSpaceLabel - ${NSD_CreateLabel} $CTL_RIGHT_PX 145u 100% $HEIGHT_PX " " + ${NSD_CreateLabel} $ControlRightPX 145u 100% $ControlHeightPX " " Pop $LabelFreeSpace SetCtlColors $LabelFreeSpace ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR} SendMessage $LabelFreeSpace ${WM_SETFONT} $FontNormal 0 @@ -705,7 +968,9 @@ Function createOptions GetDlgItem $0 $HWNDPARENT 3 ; Back and Options button SendMessage $0 ${WM_SETTEXT} 0 "STR:$(BACK_BUTTON)" - ${If} "$WasOptionsButtonClicked" != "true" + ; If the option button was not clicked display the reason for what needs to be + ; resolved to continue the installation. + ${If} "$WasOptionsButtonClicked" != "1" ${If} "$CanWriteToInstallDir" == "false" MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_WRITE_ACCESS)" ${ElseIf} "$HasRequiredSpaceAvailable" == "false" @@ -713,12 +978,18 @@ Function createOptions ${EndIf} ${EndIf} + IntOp $OptionsPageShownCount $OptionsPageShownCount + 1 + + System::Call "kernel32::GetTickCount()l .s" + Pop $StartOptionsPhaseTickCount + LockWindow off nsDialogs::Show FunctionEnd Function leaveOptions LockWindow on + ${GetRoot} "$INSTDIR" $0 ${GetLongPath} "$INSTDIR" $INSTDIR ${GetLongPath} "$0" $0 @@ -742,6 +1013,13 @@ Function leaveOptions Abort ; Stay on the page ${EndIf} + System::Call "kernel32::GetTickCount()l .s" + Pop $0 + ${GetSecondsElapsed} "$StartOptionsPhaseTickCount" "$0" $1 + ; This is added to the previous value of $OptionsPhaseSeconds because the + ; options page can be displayed multiple times. + IntOp $OptionsPhaseSeconds $OptionsPhaseSeconds + $1 + ${NSD_GetState} $CheckboxShortcutOnBar $CheckboxShortcutOnBar ${NSD_GetState} $CheckboxShortcutInStartMenu $CheckboxShortcutInStartMenu ${NSD_GetState} $CheckboxShortcutOnDesktop $CheckboxShortcutOnDesktop @@ -849,7 +1127,8 @@ Function createInstall ${NSD_CreateProgressBar} 103u 166u 157u 9u "" Pop $ProgressbarDownload ${NSD_AddStyle} $ProgressbarDownload ${PBS_MARQUEE} - SendMessage $ProgressbarDownload ${PBM_SETMARQUEE} 1 10 ; start=1|stop=0 interval(ms)=+N + SendMessage $ProgressbarDownload ${PBM_SETMARQUEE} 1 \ + ${ProgressbarMarqueeIntervalMS} ; start=1|stop=0 interval(ms)=+N ${NSD_CreateProgressBar} 260u 166u 84u 9u "" Pop $ProgressbarInstall @@ -881,7 +1160,7 @@ Function createInstall SendMessage $0 ${WM_KILLFOCUS} 0 0 ; Set as default is not supported in the installer for Win8 and above - ${If} $CanSetAsDefault == "true" + ${If} "$CanSetAsDefault" == "true" GetDlgItem $0 $HWNDPARENT 10 ; Default browser checkbox SendMessage $0 ${BM_GETCHECK} 0 0 $CheckboxSetAsDefault EnableWindow $0 0 @@ -894,13 +1173,25 @@ Function createInstall SetCtlColors $0 ${FOOTER_CONTROL_TEXT_COLOR_FADED} ${FOOTER_BKGRD_COLOR} ShowWindow $0 ${SW_SHOW} + ; Set $DownloadReset to true so the first download tick count is measured. + StrCpy $DownloadReset "true" StrCpy $IsDownloadFinished "false" - StrCpy $DownloadReset "false" - StrCpy $ExitCode "${ERR_CANCEL_DOWNLOAD}" - ${If} ${FileExists} "$INSTDIR\${FileMainEXE}" - StrCpy $ExistingInstall "1" - ${Else} - StrCpy $ExistingInstall "0" + StrCpy $DownloadRetryCount "0" + StrCpy $StartLastDownloadTickCount "0" + StrCpy $DownloadFirstTransferSeconds "" + StrCpy $ExitCode "${ERR_DOWNLOAD_CANCEL}" + StrCpy $OpenedDownloadPage "0" + + ClearErrors + ReadINIStr $ExistingVersion "$INSTDIR\application.ini" "App" "Version" + ${If} ${Errors} + StrCpy $ExistingVersion "0" + ${EndIf} + + ClearErrors + ReadINIStr $ExistingBuildID "$INSTDIR\application.ini" "App" "BuildID" + ${If} ${Errors} + StrCpy $ExistingBuildID "0" ${EndIf} ${If} ${FileExists} "$LOCALAPPDATA\Mozilla\Firefox" @@ -909,8 +1200,10 @@ Function createInstall StrCpy $ExistingProfile "0" ${EndIf} + StrCpy $DownloadServerIP "" + System::Call "kernel32::GetTickCount()l .s" - Pop $StartTickCount + Pop $StartDownloadPhaseTickCount ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS} @@ -924,14 +1217,10 @@ Function createInstall ${NSD_FreeImage} $HWndBitmapBlurb3 FunctionEnd -Function leaveInstall -# Need a ping? -FunctionEnd - Function StartDownload ${NSD_KillTimer} StartDownload InetBgDL::Get "${URLStubDownload}" "$PLUGINSDIR\download.exe" \ - /RANGEREQUEST /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END + /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END StrCpy $4 "" ${NSD_CreateTimer} OnDownload ${DownloadIntervalMS} ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}" @@ -948,37 +1237,90 @@ Function OnDownload # $4 = Size of current file (Empty string if the size is unknown) # /RESET must be used if status $0 > 299 (e.g. failure) # When status is $0 =< 299 it is handled by InetBgDL + StrCpy $DownloadServerIP "$5" ${If} $0 > 299 ${NSD_KillTimer} OnDownload + IntOp $DownloadRetryCount $DownloadRetryCount + 1 ${If} "$DownloadReset" != "true" - StrCpy $DownloadedAmount "0" + StrCpy $DownloadedBytes "0" ${NSD_AddStyle} $ProgressbarDownload ${PBS_MARQUEE} - SendMessage $ProgressbarDownload ${PBM_SETMARQUEE} 1 10 ; start=1|stop=0 interval(ms)=+N + SendMessage $ProgressbarDownload ${PBM_SETMARQUEE} 1 \ + ${ProgressbarMarqueeIntervalMS} ; start=1|stop=0 interval(ms)=+N ${EndIf} InetBgDL::Get /RESET /END - ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS} + StrCpy $DownloadSizeBytes "" StrCpy $DownloadReset "true" - StrCpy $DownloadSize "" + + ${If} $DownloadRetryCount >= ${DownloadMaxRetries} + StrCpy $ExitCode "${ERR_DOWNLOAD_TOO_MANY_RETRIES}" + ; Use a timer so the UI has a chance to update + ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS} + ${Else} + ${NSD_CreateTimer} StartDownload ${DownloadRetryIntervalMS} + ${EndIf} Return ${EndIf} ${If} "$DownloadReset" == "true" + System::Call "kernel32::GetTickCount()l .s" + Pop $StartLastDownloadTickCount StrCpy $DownloadReset "false" + ; The seconds elapsed from the start of the download phase until the first + ; bytes are received are only recorded for the first request so it is + ; possible to determine connection issues for the first request. + ${If} "$DownloadFirstTransferSeconds" == "" + ; Get the seconds elapsed from the start of the download phase until the + ; first bytes are received. + ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$StartLastDownloadTickCount" $DownloadFirstTransferSeconds + ${EndIf} ${EndIf} - ${If} $DownloadSize == "" - ${AndIf} $4 != "" - StrCpy $DownloadSize "$4" + ${If} "$DownloadSizeBytes" == "" + ${AndIf} "$4" != "" + ; Handle the case where the size of the file to be downloaded is less than + ; the minimum expected size or greater than the maximum expected size at the + ; beginning of the download. + ${If} $4 < ${DownloadMinSizeBytes} + ${OrIf} $4 > ${DownloadMaxSizeBytes} + ${NSD_KillTimer} OnDownload + InetBgDL::Get /RESET /END + StrCpy $DownloadReset "true" + + ${If} $DownloadRetryCount >= ${DownloadMaxRetries} + ; Use a timer so the UI has a chance to update + ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS} + ${Else} + ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS} + ${EndIf} + Return + ${EndIf} + + StrCpy $DownloadSizeBytes "$4" System::Int64Op $4 / 2 Pop $HalfOfDownload SendMessage $ProgressbarDownload ${PBM_SETMARQUEE} 0 0 ; start=1|stop=0 interval(ms)=+N ${RemoveStyle} $ProgressbarDownload ${PBS_MARQUEE} - SendMessage $ProgressbarDownload ${PBM_SETRANGE32} 0 $DownloadSize + SendMessage $ProgressbarDownload ${PBM_SETRANGE32} 0 $DownloadSizeBytes ${EndIf} ; Don't update the status until after the download starts ${If} $2 != 0 - ${AndIf} $4 == "" + ${AndIf} "$4" == "" + Return + ${EndIf} + + ; Handle the case where the downloaded size is greater than the maximum + ; expected size during the download. + ${If} $DownloadedBytes > ${DownloadMaxSizeBytes} + InetBgDL::Get /RESET /END + StrCpy $DownloadReset "true" + + ${If} $DownloadRetryCount >= ${DownloadMaxRetries} + ; Use a timer so the UI has a chance to update + ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS} + ${Else} + ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS} + ${EndIf} Return ${EndIf} @@ -991,12 +1333,31 @@ Function OnDownload ; feedback. StrCpy $InstallCounterStep "${InstallProgressFirstStep}" System::Call "kernel32::GetTickCount()l .s" - Pop $DownloadTickCount - StrCpy $DownloadedAmount "$DownloadSize" + Pop $EndDownloadPhaseTickCount + + StrCpy $DownloadedBytes "$DownloadSizeBytes" + + ; When a download has finished handle the case where the downloaded size + ; is less than the minimum expected size or greater than the maximum + ; expected size during the download. + ${If} $DownloadedBytes < ${DownloadMinSizeBytes} + ${OrIf} $DownloadedBytes > ${DownloadMaxSizeBytes} + InetBgDL::Get /RESET /END + StrCpy $DownloadReset "true" + + ${If} $DownloadRetryCount >= ${DownloadMaxRetries} + ; Use a timer so the UI has a chance to update + ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS} + ${Else} + ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS} + ${EndIf} + Return + ${EndIf} + LockWindow on ; Update the progress bars first in the UI change so they take affect ; before other UI changes. - SendMessage $ProgressbarDownload ${PBM_SETPOS} $DownloadSize 0 + SendMessage $ProgressbarDownload ${PBM_SETPOS} $DownloadSizeBytes 0 SendMessage $ProgressbarInstall ${PBM_SETPOS} $InstallCounterStep 0 ShowWindow $LabelDownloadingInProgress ${SW_HIDE} ShowWindow $LabelInstallingToBeDone ${SW_HIDE} @@ -1020,7 +1381,7 @@ Function OnDownload StrCpy $HandleDownload "$R9" ${If} $HandleDownload == ${INVALID_HANDLE_VALUE} - StrCpy $ExitCode "${ERR_INVALID_HANDLE}" + StrCpy $ExitCode "${ERR_PREINSTALL_INVALID_HANDLE}" StrCpy $0 "0" StrCpy $1 "0" ${Else} @@ -1031,14 +1392,17 @@ Function OnDownload Pop $1 ${If} $0 == 0 ${AndIf} $1 == 0 - StrCpy $ExitCode "${ERR_CERT_UNTRUSTED_AND_ATTRIBUTES}" + StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED_AND_ATTRIBUTES}" ${ElseIf} $0 == 0 - StrCpy $ExitCode "${ERR_CERT_UNTRUSTED}" + StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED}" ${ElseIf} $1 == 0 - StrCpy $ExitCode "${ERR_CERT_ATTRIBUTES}" + StrCpy $ExitCode "${ERR_PREINSTALL_CERT_ATTRIBUTES}" ${EndIf} ${EndIf} + System::Call "kernel32::GetTickCount()l .s" + Pop $EndPreInstallPhaseTickCount + ${If} $0 == 0 ${OrIf} $1 == 0 ; Use a timer so the UI has a chance to update @@ -1107,15 +1471,40 @@ Function OnDownload ShowWindow $BitmapBlurb2 ${SW_SHOW} LockWindow off ${EndIf} - StrCpy $DownloadedAmount "$3" + StrCpy $DownloadedBytes "$3" SendMessage $ProgressbarDownload ${PBM_SETPOS} $3 0 ${EndIf} ${EndIf} FunctionEnd +Function OnPing + InetBgDL::GetStats + # $0 = HTTP status code, 0=Completed + # $1 = Completed files + # $2 = Remaining files + # $3 = Number of downloaded bytes for the current file + # $4 = Size of current file (Empty string if the size is unknown) + # /RESET must be used if status $0 > 299 (e.g. failure) + # When status is $0 =< 299 it is handled by InetBgDL + ${If} $2 == 0 + ${OrIf} $0 > 299 + ${NSD_KillTimer} OnPing + ${If} $0 > 299 + InetBgDL::Get /RESET /END + ${EndIf} + ; The following will exit the installer + SetAutoClose true + StrCpy $R9 "2" + Call RelativeGotoPage + ${EndIf} +FunctionEnd + Function StartInstall ${NSD_KillTimer} StartInstall + System::Call "kernel32::GetTickCount()l .s" + Pop $EndPreInstallPhaseTickCount + IntOp $InstallCounterStep $InstallCounterStep + 1 LockWindow on SendMessage $ProgressbarInstall ${PBM_SETPOS} $InstallCounterStep 0 @@ -1127,11 +1516,11 @@ FunctionEnd Function CheckInstall IntOp $InstallCounterStep $InstallCounterStep + 1 - ${If} $InstallCounterStep >= ${InstallProgresSteps} + ${If} $InstallCounterStep >= ${InstallProgresSteps} ${NSD_KillTimer} CheckInstall ; Close the handle that prevents modification of the full installer System::Call 'kernel32::CloseHandle(i $HandleDownload)' - StrCpy $ExitCode "${ERR_CHECK_INSTALL_TIMEOUT}" + StrCpy $ExitCode "${ERR_INSTALL_TIMEOUT}" ; Use a timer so the UI has a chance to update ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS} Return @@ -1155,7 +1544,7 @@ Function CheckInstall Delete "$PLUGINSDIR\download.exe" Delete "$PLUGINSDIR\${CONFIG_INI}" System::Call "kernel32::GetTickCount()l .s" - Pop $InstallTickCount + Pop $EndInstallPhaseTickCount ${NSD_CreateTimer} FinishInstall ${InstallIntervalMS} ${EndUnless} ${EndIf} @@ -1212,14 +1601,11 @@ Function FinishInstall Call LaunchApp - ; The following will exit the installer - SetAutoClose true - StrCpy $R9 "2" - Call RelativeGotoPage + Call SendPing FunctionEnd Function OnBack - StrCpy $WasOptionsButtonClicked "true" + StrCpy $WasOptionsButtonClicked "1" StrCpy $R9 "1" ; Goto the next page Call RelativeGotoPage ; The call to Abort prevents NSIS from trying to move to the previous or the @@ -1424,12 +1810,12 @@ FunctionEnd Function LaunchApp FindWindow $0 "${WindowClass}" ${If} $0 <> 0 ; integer comparison - StrCpy $FirefoxLaunch "1" + StrCpy $FirefoxLaunchCode "1" MessageBox MB_OK|MB_ICONQUESTION "$(WARN_MANUALLY_CLOSE_APP_LAUNCH)" Return ${EndIf} - StrCpy $FirefoxLaunch "2" + StrCpy $FirefoxLaunchCode "2" ; Set the current working directory to the installation directory SetOutPath "$INSTDIR" @@ -1458,11 +1844,10 @@ FunctionEnd Function DisplayDownloadError ${NSD_KillTimer} DisplayDownloadError - StrCpy $R9 "false" - MessageBox MB_OKCANCEL|MB_ICONSTOP "$(ERROR_DOWNLOAD)" IDCANCEL +2 IDOK 0 - StrCpy $R9 "true" + MessageBox MB_OKCANCEL|MB_ICONSTOP "$(ERROR_DOWNLOAD)" IDCANCEL +2 IDOK +1 + StrCpy $OpenedDownloadPage "1" ; Already initialized to 0 - ${If} "$R9" == "true" + ${If} "$OpenedDownloadPage" == "1" ClearErrors ${GetParameters} $0 ${GetOptions} "$0" "/UAC:" $1 @@ -1474,9 +1859,7 @@ Function DisplayDownloadError ${EndIf} ${EndIf} - SetAutoClose true - StrCpy $R9 "2" - Call RelativeGotoPage + Call SendPing FunctionEnd Function OpenManualDownloadURL diff --git a/other-licenses/nsis/Contrib/InetBgDL/InetBgDL.cpp b/other-licenses/nsis/Contrib/InetBgDL/InetBgDL.cpp index 3ed34798f59..e4f61d6c62b 100644 --- a/other-licenses/nsis/Contrib/InetBgDL/InetBgDL.cpp +++ b/other-licenses/nsis/Contrib/InetBgDL/InetBgDL.cpp @@ -1,768 +1,668 @@ -// -// Copyright (C) Anders Kjersem. Licensed under the zlib/libpng license -// - -#include "InetBgDL.h" - -#define USERAGENT _T("NSIS InetBgDL (Mozilla)") - -#define STATUS_COMPLETEDALL 0 -#define STATUS_INITIAL 202 -#define STATUS_CONNECTING STATUS_INITIAL //102 -#define STATUS_DOWNLOADING STATUS_INITIAL -#define STATUS_ERR_GETLASTERROR 418 //HTTP: I'm a teapot: Win32 error code in $3 -#define STATUS_ERR_LOCALFILEWRITEERROR 450 //HTTP: MS parental control extension -#define STATUS_ERR_CANCELLED 499 - -typedef DWORD FILESIZE_T; // Limit to 4GB for now... -#define FILESIZE_UNKNOWN (-1) - -HINSTANCE g_hInst; -NSIS::stack_t*g_pLocations = NULL; -HANDLE g_hThread = NULL; -HANDLE g_hGETStartedEvent = NULL; -volatile UINT g_FilesTotal = 0; -volatile UINT g_FilesCompleted = 0; -volatile UINT g_Status = STATUS_INITIAL; -volatile FILESIZE_T g_cbCurrXF; -volatile FILESIZE_T g_cbCurrTot = FILESIZE_UNKNOWN; -CRITICAL_SECTION g_CritLock; -UINT g_N_CCH; -PTSTR g_N_Vars; - -DWORD g_ConnectTimeout = 0; -DWORD g_ReceiveTimeout = 0; -bool g_WantRangeRequest = false; - -#define NSISPI_INITGLOBALS(N_CCH, N_Vars) do { \ - g_N_CCH = N_CCH; \ - g_N_Vars = N_Vars; \ - } while(0) - -#define ONELOCKTORULETHEMALL -#ifdef ONELOCKTORULETHEMALL -#define TaskLock_AcquireExclusive() EnterCriticalSection(&g_CritLock) -#define TaskLock_ReleaseExclusive() LeaveCriticalSection(&g_CritLock) -#define StatsLock_AcquireExclusive() TaskLock_AcquireExclusive() -#define StatsLock_ReleaseExclusive() TaskLock_ReleaseExclusive() -#define StatsLock_AcquireShared() StatsLock_AcquireExclusive() -#define StatsLock_ReleaseShared() StatsLock_ReleaseExclusive() -#endif - -PTSTR NSIS_SetRegStr(UINT Reg, LPCTSTR Value) -{ - PTSTR s = g_N_Vars + (Reg * g_N_CCH); - lstrcpy(s, Value); - return s; -} -#define NSIS_SetRegStrEmpty(r) NSIS_SetRegStr(r, _T("")) -void NSIS_SetRegUINT(UINT Reg, UINT Value) -{ - TCHAR buf[32]; - wsprintf(buf, _T("%u"), Value); - NSIS_SetRegStr(Reg, buf); -} -#define StackFreeItem(pI) GlobalFree(pI) -NSIS::stack_t* StackPopItem(NSIS::stack_t**ppST) -{ - if (*ppST) - { - NSIS::stack_t*pItem = *ppST; - *ppST = pItem->next; - return pItem; - } - return NULL; -} - -void Reset() -{ - // The g_hGETStartedEvent event is used to make sure that the Get() call will - // acquire the lock before the Reset() call acquires the lock. - if (g_hGETStartedEvent) { - WaitForSingleObject(g_hGETStartedEvent, INFINITE); - CloseHandle(g_hGETStartedEvent); - g_hGETStartedEvent = NULL; - } - - TaskLock_AcquireExclusive(); -#ifndef ONELOCKTORULETHEMALL - StatsLock_AcquireExclusive(); -#endif - g_FilesTotal = 0; // This causes the Task thread to exit the transfer loop - if (g_hThread) - { - if (WAIT_OBJECT_0 != WaitForSingleObject(g_hThread, 10 * 1000)) - { - TerminateThread(g_hThread, ERROR_OPERATION_ABORTED); - } - CloseHandle(g_hThread); - g_hThread = NULL; - } - g_FilesTotal = 0; - g_FilesCompleted = 0; - g_Status = STATUS_INITIAL; -#ifndef ONELOCKTORULETHEMALL - StatsLock_ReleaseExclusive(); -#endif - for (NSIS::stack_t*pTmpTast,*pTask = g_pLocations; pTask ;) - { - pTmpTast = pTask; - pTask = pTask->next; - StackFreeItem(pTmpTast); - } - g_pLocations = NULL; - TaskLock_ReleaseExclusive(); -} - -UINT_PTR __cdecl NSISPluginCallback(UINT Event) -{ - switch(Event) - { - case NSPIM_UNLOAD: - Reset(); - break; - } - return NULL; -} - -/* ParseURL is a quickly thrown together simple way to parse a URL into parts. - * It is only designed to support simple URLs and doesn't support things like - * authorization information in the URL. - * The format of the URL is assumed to be: - * ://:/ - * - * @param url The input URL to parse - * @param protocol Out variable which will store the protocol of the passed url - * @param server Out variable which will store the server of the passed url - * @param port Out variable which will store the port of the passed url - * @param path Out variable which will store the path of the passed url - * @return true if successful -*/ -template static bool -BasicParseURL(LPCWSTR url, wchar_t (*protocol)[A], INTERNET_PORT *port, - wchar_t (*server)[B], wchar_t (*path)[C]) -{ - ZeroMemory(*protocol, A * sizeof(wchar_t)); - ZeroMemory(*server, B * sizeof(wchar_t)); - ZeroMemory(*path, C * sizeof(wchar_t)); - - const WCHAR *p = url; - // Skip past the protocol - int pos = 0; - while (*p != L'\0' && *p != L'/' && *p != L':') - { - if (pos + 1 >= A) - return false; - (*protocol)[pos++] = *p++; - } - - // Skip past the :// - p += 3; - - *port = INTERNET_DEFAULT_HTTP_PORT; - if (!wcsicmp(*protocol, L"https")) - { - *port = INTERNET_DEFAULT_HTTPS_PORT; - } - - // Get the server - pos = 0; - while (*p != L'\0' && *p != L'/' && *p != L':') - { - if (pos + 1 >= B) - return false; - (*server)[pos++] = *p++; - } - - // Get the port if specified - if (*p == L':') - { - WCHAR portStr[16]; - p++; - pos = 0; - while (*p != L'\0' && *p != L'/') - { - if (pos + 1 >= 16) - return false; - portStr[pos++] = *p++; - } - portStr[pos] = '\0'; - *port = (INTERNET_PORT)_wtoi(portStr); - } - else - { - // Skip the slash after the server - while (*p != L'\0' && *p != L'/') - { - p++; - } - } - - // Get the rest as the path - pos = 0; - while (*p != L'\0') - { - if (pos + 1 >= C) - return false; - (*path)[pos++] = *p++; - } - - return true; -} - -DWORD CALLBACK TaskThreadProc(LPVOID ThreadParam) -{ - NSIS::stack_t *pURL,*pFile; - HINTERNET hInetSes = NULL, hInetCon = NULL; - HANDLE hLocalFile; - bool completedFile = false; -startnexttask: - hLocalFile = INVALID_HANDLE_VALUE; - pFile = NULL; - TaskLock_AcquireExclusive(); - // Now that we've acquired the lock, we can set the event to indicate this. - // SetEvent will likely never fail, but if it does we should set it to NULL - // to avoid anyone waiting on it. - if (!SetEvent(g_hGETStartedEvent)) { - CloseHandle(g_hGETStartedEvent); - g_hGETStartedEvent = NULL; - } - pURL = g_pLocations; - if (pURL) - { - pFile = pURL->next; - g_pLocations = pFile->next; - } -#ifndef ONELOCKTORULETHEMALL - StatsLock_AcquireExclusive(); -#endif - if (completedFile) - { - ++g_FilesCompleted; - } - completedFile = false; - g_cbCurrXF = 0; - g_cbCurrTot = FILESIZE_UNKNOWN; - if (!pURL) - { - if (g_FilesTotal) - { - if (g_FilesTotal == g_FilesCompleted) - { - g_Status = STATUS_COMPLETEDALL; - } - } - g_hThread = NULL; - } -#ifndef ONELOCKTORULETHEMALL - StatsLock_ReleaseExclusive(); -#endif - TaskLock_ReleaseExclusive(); - - if (!pURL) - { - if (0) - { -diegle: - DWORD gle = GetLastError(); - //TODO? if (ERROR_INTERNET_EXTENDED_ERROR==gle) InternetGetLastResponseInfo(...) - g_Status = STATUS_ERR_GETLASTERROR; - } - if (hInetSes) - { - InternetCloseHandle(hInetSes); - } - if (hInetCon) - { - InternetCloseHandle(hInetCon); - } - if (INVALID_HANDLE_VALUE != hLocalFile) - { - CloseHandle(hLocalFile); - } - StackFreeItem(pURL); - StackFreeItem(pFile); - return 0; - } - - if (!hInetSes) - { - hInetSes = InternetOpen(USERAGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); - if (!hInetSes) - { - goto diegle; - } - - //msdn.microsoft.com/library/default.asp?url=/workshop/components/offline/offline.asp#Supporting Offline Browsing in Applications and Components - ULONG longOpt; - DWORD cbio = sizeof(ULONG); - if (InternetQueryOption(hInetSes, INTERNET_OPTION_CONNECTED_STATE, &longOpt, &cbio)) - { - if (INTERNET_STATE_DISCONNECTED_BY_USER&longOpt) - { - INTERNET_CONNECTED_INFO ci = {INTERNET_STATE_CONNECTED, 0}; - InternetSetOption(hInetSes, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci)); - } - } - - if(g_ConnectTimeout > 0) - { - InternetSetOption(hInetSes, INTERNET_OPTION_CONNECT_TIMEOUT, - &g_ConnectTimeout, sizeof(g_ConnectTimeout)); - } - } - - DWORD ec = ERROR_SUCCESS; - hLocalFile = CreateFile(pFile->text,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_DELETE,NULL,CREATE_ALWAYS,0,NULL); - if (INVALID_HANDLE_VALUE == hLocalFile) - { - goto diegle; - } - - const DWORD IOURedirFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | - INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; - const DWORD IOUCacheFlags = INTERNET_FLAG_RESYNCHRONIZE | - INTERNET_FLAG_NO_CACHE_WRITE | - INTERNET_FLAG_PRAGMA_NOCACHE | - INTERNET_FLAG_RELOAD; - const DWORD IOUCookieFlags = INTERNET_FLAG_NO_COOKIES; - DWORD IOUFlags = IOURedirFlags | IOUCacheFlags | IOUCookieFlags | - INTERNET_FLAG_NO_UI | INTERNET_FLAG_EXISTING_CONNECT; - - WCHAR protocol[16]; - WCHAR server[128]; - WCHAR path[1024]; - INTERNET_PORT port; - // Good old VC6 cannot deduce the size of these params :'( - if (!BasicParseURL<16, 128, 1024>(pURL->text, &protocol, &port, &server, &path)) - { - // Insufficient buffer or bad URL passed in - goto diegle; - } - - DWORD context; - hInetCon = InternetConnect(hInetSes, server, port, NULL, NULL, - INTERNET_SERVICE_HTTP, IOUFlags, - (unsigned long)&context); - if (!hInetCon) - { - goto diegle; - } - - // Setup a buffer of size 256KiB to store the downloaded data. - // Get at most 4MiB at a time from the partial HTTP Range requests. - // Biffer buffers will be faster. - // cbRangeReadBufXF should be a multiple of cbBufXF. - const UINT cbBufXF = 262144; - const UINT cbRangeReadBufXF = 4194304; - BYTE bufXF[cbBufXF]; - - // Up the default internal buffer size from 4096 to internalReadBufferSize. - DWORD internalReadBufferSize = cbRangeReadBufXF; - if (!InternetSetOption(hInetCon, INTERNET_OPTION_READ_BUFFER_SIZE, - &internalReadBufferSize, sizeof(DWORD))) - { - // Maybe it's too big, try half of the optimal value. If that fails just - // use the default. - internalReadBufferSize /= 2; - InternetSetOption(hInetCon, INTERNET_OPTION_READ_BUFFER_SIZE, - &internalReadBufferSize, sizeof(DWORD)); - } - - // Change the default timeout of 30 seconds to the specified value. - // Is case a proxy in between caches the results, it could in theory - // take longer to get the first chunk, so it is good to set this high. - if (g_ReceiveTimeout) - { - InternetSetOption(hInetCon, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT, - &g_ReceiveTimeout, sizeof(DWORD)); - } - - HINTERNET hInetFile; - DWORD cbio = sizeof(DWORD); - - // Keep looping until we don't have a redirect anymore - int redirectCount = 0; - for (;;) { - // Make sure we aren't stuck in some kind of infinite redirect loop. - if (redirectCount > 15) { - goto diegle; - } - - // If a range request was specified, first do a HEAD request - hInetFile = HttpOpenRequest(hInetCon, g_WantRangeRequest ? L"HEAD" : L"GET", - path, NULL, NULL, NULL, - INTERNET_FLAG_NO_AUTO_REDIRECT | - INTERNET_FLAG_PRAGMA_NOCACHE | - INTERNET_FLAG_RELOAD | - (port == INTERNET_DEFAULT_HTTPS_PORT ? - INTERNET_FLAG_SECURE : 0), 0); - if (!hInetFile) - { - goto diegle; - } - - if (!HttpSendRequest(hInetFile, NULL, 0, NULL, 0)) - { - goto diegle; - } - - WCHAR responseText[256]; - cbio = sizeof(responseText); - if (!HttpQueryInfo(hInetFile, - HTTP_QUERY_STATUS_CODE, - responseText, &cbio, NULL)) - { - goto diegle; - } - - int statusCode = _wtoi(responseText); - if (statusCode == HTTP_STATUS_REDIRECT || - statusCode == HTTP_STATUS_MOVED) { - redirectCount++; - WCHAR URLBuffer[2048]; - cbio = sizeof(URLBuffer); - if (!HttpQueryInfo(hInetFile, - HTTP_QUERY_LOCATION, - (DWORD*)URLBuffer, &cbio, NULL)) - { - goto diegle; - } - - WCHAR protocol2[16]; - WCHAR server2[128]; - WCHAR path2[1024]; - INTERNET_PORT port2; - BasicParseURL<16, 128, 1024>(URLBuffer, &protocol2, &port2, &server2, &path2); - // Check if we need to reconnect to a new server - if (wcscmp(protocol, protocol2) || wcscmp(server, server2) || - port != port2) { - wcscpy(server, server2); - port = port2; - InternetCloseHandle(hInetCon); - hInetCon = InternetConnect(hInetSes, server, port, NULL, NULL, - INTERNET_SERVICE_HTTP, IOUFlags, - (unsigned long)&context); - if (!hInetCon) - { - goto diegle; - } - } - wcscpy(path, path2); - - // Close the existing handle because we'll be issuing a new request - // with the new request path. - InternetCloseHandle(hInetFile); - continue; - } - break; - } - - // Get the file length via the Content-Length header - FILESIZE_T cbThisFile; - cbio = sizeof(cbThisFile); - if (!HttpQueryInfo(hInetFile, - HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, - &cbThisFile, &cbio, NULL)) - { - cbThisFile = FILESIZE_UNKNOWN; - } - - // Determine if we should use byte range requests. We want to use it if: - // 1. Server accepts byte range requests - // 2. The size of the file is known and more than our Range buffer size. - bool shouldUseRangeRequest = true; - WCHAR rangeRequestAccepted[64] = { '\0' }; - cbio = sizeof(rangeRequestAccepted); - if (cbThisFile != FILESIZE_UNKNOWN && cbThisFile <= cbRangeReadBufXF || - !HttpQueryInfo(hInetFile, - HTTP_QUERY_ACCEPT_RANGES, - (LPDWORD)rangeRequestAccepted, &cbio, NULL)) - { - shouldUseRangeRequest = false; - } - else - { - shouldUseRangeRequest = wcsstr(rangeRequestAccepted, L"bytes") != 0 && - cbThisFile != FILESIZE_UNKNOWN; - } - - // If the server doesn't have range request support or doesn't have a - // Content-Length header, then get everything all at once. - // If the user didn't want a range request, then we already issued the GET - // request earlier. If the user did want a range request, then we issued only - // a HEAD so far. - if (g_WantRangeRequest && !shouldUseRangeRequest) - { - InternetCloseHandle(hInetFile); - InternetCloseHandle(hInetCon); - hInetFile = InternetOpenUrl(hInetSes, pURL->text, - NULL, 0, IOUFlags | - (!wcsicmp(protocol, L"https") ? - INTERNET_FLAG_SECURE : 0), - NULL); - if (!hInetFile) - { - goto diegle; - } - - // For some reason this also needs to be set on the hInetFile and - // not just the connection. - if (g_ReceiveTimeout > 0) - { - InternetSetOption(hInetCon, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT, - &g_ReceiveTimeout, sizeof(DWORD)); - } - } - - for(;;) - { - DWORD cbio = 0,cbXF = 0; - // If we know the file size, download it in chunks - if (g_WantRangeRequest && shouldUseRangeRequest && cbThisFile != g_cbCurrXF) - { - // Close the previous request, but not the connection - InternetCloseHandle(hInetFile); - hInetFile = HttpOpenRequest(hInetCon, L"GET", path, - NULL, NULL, NULL, - INTERNET_FLAG_PRAGMA_NOCACHE | - INTERNET_FLAG_RELOAD | - (port == INTERNET_DEFAULT_HTTPS_PORT ? - INTERNET_FLAG_SECURE : 0), 0); - if (!hInetFile) - { - // TODO: we could add retry here to be more tolerant - goto diegle; - } - - // For some reason this also needs to be set on the hInetFile and - // not just the connection. - if (g_ReceiveTimeout > 0) - { - InternetSetOption(hInetCon, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT, - &g_ReceiveTimeout, sizeof(DWORD)); - } - - WCHAR range[32]; - swprintf(range, L"Range: bytes=%d-%d", g_cbCurrXF, - min(g_cbCurrXF + cbRangeReadBufXF, cbThisFile)); - if (!HttpSendRequest(hInetFile, range, wcslen(range), NULL, 0)) - { - // TODO: we could add retry here to be more tolerant - goto diegle; - } - } - - // Read the chunk (or full file if we don't know the size) we downloaded - BOOL retXF; - for (;;) - { - DWORD cbioThisIteration = 0; - retXF = InternetReadFile(hInetFile, bufXF, cbBufXF, &cbioThisIteration); - if (!retXF) - { - ec = GetLastError(); - TRACE1(_T("InternetReadFile failed, gle=%u\n"), ec); - // TODO: we could add retry here to be more tolerant - goto diegle; - } - - // Check if we're done reading - if (cbioThisIteration == 0) - { - break; - } - - // Write what we found - cbXF = cbioThisIteration; - retXF = WriteFile(hLocalFile, bufXF, cbXF, &cbioThisIteration, NULL); - if (!retXF || cbXF != cbioThisIteration) - { - cbio += cbioThisIteration; - ec = GetLastError(); - break; - } - - cbio += cbioThisIteration; - StatsLock_AcquireExclusive(); - if (FILESIZE_UNKNOWN != cbThisFile) - { - g_cbCurrTot = cbThisFile; - } - g_cbCurrXF += cbXF; - StatsLock_ReleaseExclusive(); - - // Avoid an extra call to InternetReadFile if we already read everything - // in the current request - if (cbio == cbRangeReadBufXF && shouldUseRangeRequest) - { - break; - } - } - - // Check if we're done transferring the file successfully - if (0 == cbio && - (cbThisFile == FILESIZE_UNKNOWN || cbThisFile == g_cbCurrXF)) - { - ASSERT(ERROR_SUCCESS == ec); - TRACE2(_T("InternetReadFile true with 0 cbio, cbThisFile=%d gle=%u\n"), cbThisFile, GetLastError()); - break; - } - - // Check if we canceled the download - if (0 == g_FilesTotal) - { - TRACEA("0==g_FilesTotal, aborting transfer loop...\n"); - ec = ERROR_CANCELLED; - break; - } - } - - TRACE2(_T("TaskThreadProc completed %s, ec=%u\n"), pURL->text, ec); - InternetCloseHandle(hInetFile); - if (ERROR_SUCCESS == ec) - { - if (INVALID_HANDLE_VALUE != hLocalFile) - { - CloseHandle(hLocalFile); - hLocalFile = INVALID_HANDLE_VALUE; - } - StackFreeItem(pURL); - StackFreeItem(pFile); - ++completedFile; - } - else - { - SetLastError(ec); - goto diegle; - } - goto startnexttask; -} - -NSISPIEXPORTFUNC Get(HWND hwndNSIS, UINT N_CCH, TCHAR*N_Vars, NSIS::stack_t**ppST, NSIS::xparams_t*pX) -{ - pX->RegisterPluginCallback(g_hInst, NSISPluginCallback); - g_WantRangeRequest = false; - for (;;) - { - NSIS::stack_t*pURL = StackPopItem(ppST); - if (!pURL) - { - break; - } - - if (lstrcmpi(pURL->text, _T("/rangerequest")) == 0) - { - g_WantRangeRequest = true; - continue; - } - else if (lstrcmpi(pURL->text, _T("/connecttimeout")) == 0) - { - NSIS::stack_t*pConnectTimeout = StackPopItem(ppST); - g_ConnectTimeout = _tcstol(pConnectTimeout->text, NULL, 10) * 1000; - continue; - } - else if (lstrcmpi(pURL->text, _T("/receivetimeout")) == 0) - { - NSIS::stack_t*pReceiveTimeout = StackPopItem(ppST); - g_ReceiveTimeout = _tcstol(pReceiveTimeout->text, NULL, 10) * 1000; - continue; - } - else if (lstrcmpi(pURL->text, _T("/reset")) == 0) - { - StackFreeItem(pURL); - Reset(); - continue; - } - else if (lstrcmpi(pURL->text, _T("/end")) == 0) - { -freeurlandexit: - StackFreeItem(pURL); - break; - } - - NSIS::stack_t*pFile = StackPopItem(ppST); - if (!pFile) - { - goto freeurlandexit; - } - - TaskLock_AcquireExclusive(); - - pFile->next = NULL; - pURL->next = pFile; - NSIS::stack_t*pTasksTail = g_pLocations; - while(pTasksTail && pTasksTail->next) pTasksTail = pTasksTail->next; - if (pTasksTail) - { - pTasksTail->next = pURL; - } - else - { - g_pLocations = pURL; - } - - if (!g_hThread) - { - DWORD tid; - if (g_hGETStartedEvent) { - CloseHandle(g_hGETStartedEvent); - } - g_hGETStartedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - g_hThread = CreateThread(NULL, 0, TaskThreadProc, NULL, 0, &tid); - } - - if (!g_hThread) - { - goto freeurlandexit; - } - -#ifndef ONELOCKTORULETHEMALL - StatsLock_AcquireExclusive(); -#endif - ++g_FilesTotal; -#ifndef ONELOCKTORULETHEMALL - StatsLock_ReleaseExclusive(); -#endif - TaskLock_ReleaseExclusive(); - } -} - -NSISPIEXPORTFUNC GetStats(HWND hwndNSIS, UINT N_CCH, TCHAR*N_Vars, NSIS::stack_t**ppST, NSIS::xparams_t*pX) -{ - NSISPI_INITGLOBALS(N_CCH, N_Vars); - StatsLock_AcquireShared(); - NSIS_SetRegUINT(0, g_Status); - NSIS_SetRegUINT(1, g_FilesCompleted); - NSIS_SetRegUINT(2, g_FilesTotal - g_FilesCompleted); - NSIS_SetRegUINT(3, g_cbCurrXF); - NSIS_SetRegStrEmpty(4); - if (FILESIZE_UNKNOWN != g_cbCurrTot) - { - NSIS_SetRegUINT(4, g_cbCurrTot); - } - StatsLock_ReleaseShared(); -} - -EXTERN_C BOOL WINAPI _DllMainCRTStartup(HMODULE hInst, UINT Reason, LPVOID pCtx) -{ - if (DLL_PROCESS_ATTACH==Reason) - { - g_hInst=hInst; - InitializeCriticalSection(&g_CritLock); - } - return TRUE; -} - -BOOL WINAPI DllMain(HINSTANCE hInst, ULONG Reason, LPVOID pCtx) -{ - return _DllMainCRTStartup(hInst, Reason, pCtx); -} - -// For some reason VC6++ doesn't like wcsicmp and swprintf. -// If you use them, you get a linking error about _main -// as an unresolved external. -int main(int argc, char**argv) -{ - return 0; -} +// +// Copyright (C) Anders Kjersem. Licensed under the zlib/libpng license +// + +#include "InetBgDL.h" + +#define USERAGENT _T("NSIS InetBgDL (Mozilla)") + +#define STATUS_COMPLETEDALL 0 +#define STATUS_INITIAL 202 +#define STATUS_CONNECTING STATUS_INITIAL //102 +#define STATUS_DOWNLOADING STATUS_INITIAL +#define STATUS_ERR_GETLASTERROR 418 //HTTP: I'm a teapot: Win32 error code in $3 +#define STATUS_ERR_LOCALFILEWRITEERROR 450 //HTTP: MS parental control extension +#define STATUS_ERR_CANCELLED 499 + +typedef DWORD FILESIZE_T; // Limit to 4GB for now... +#define FILESIZE_UNKNOWN (-1) + +#define MAX_STRLEN 1024 + +HINSTANCE g_hInst; +NSIS::stack_t*g_pLocations = NULL; +HANDLE g_hThread = NULL; +HANDLE g_hGETStartedEvent = NULL; +volatile UINT g_FilesTotal = 0; +volatile UINT g_FilesCompleted = 0; +volatile UINT g_Status = STATUS_INITIAL; +volatile FILESIZE_T g_cbCurrXF; +volatile FILESIZE_T g_cbCurrTot = FILESIZE_UNKNOWN; +CRITICAL_SECTION g_CritLock; +UINT g_N_CCH; +PTSTR g_N_Vars; +TCHAR g_ServerIP[128] = { _T('\0') }; + +DWORD g_ConnectTimeout = 0; +DWORD g_ReceiveTimeout = 0; + +#define NSISPI_INITGLOBALS(N_CCH, N_Vars) do { \ + g_N_CCH = N_CCH; \ + g_N_Vars = N_Vars; \ + } while(0) + +#define ONELOCKTORULETHEMALL +#ifdef ONELOCKTORULETHEMALL +#define TaskLock_AcquireExclusive() EnterCriticalSection(&g_CritLock) +#define TaskLock_ReleaseExclusive() LeaveCriticalSection(&g_CritLock) +#define StatsLock_AcquireExclusive() TaskLock_AcquireExclusive() +#define StatsLock_ReleaseExclusive() TaskLock_ReleaseExclusive() +#define StatsLock_AcquireShared() StatsLock_AcquireExclusive() +#define StatsLock_ReleaseShared() StatsLock_ReleaseExclusive() +#endif + +PTSTR NSIS_SetRegStr(UINT Reg, LPCTSTR Value) +{ + PTSTR s = g_N_Vars + (Reg * g_N_CCH); + lstrcpy(s, Value); + return s; +} +#define NSIS_SetRegStrEmpty(r) NSIS_SetRegStr(r, _T("")) +void NSIS_SetRegUINT(UINT Reg, UINT Value) +{ + TCHAR buf[32]; + wsprintf(buf, _T("%u"), Value); + NSIS_SetRegStr(Reg, buf); +} +#define StackFreeItem(pI) GlobalFree(pI) +NSIS::stack_t* StackPopItem(NSIS::stack_t**ppST) +{ + if (*ppST) + { + NSIS::stack_t*pItem = *ppST; + *ppST = pItem->next; + return pItem; + } + return NULL; +} + +void Reset() +{ + // The g_hGETStartedEvent event is used to make sure that the Get() call will + // acquire the lock before the Reset() call acquires the lock. + if (g_hGETStartedEvent) { + TRACE(_T("InetBgDl: waiting on g_hGETStartedEvent\n")); + WaitForSingleObject(g_hGETStartedEvent, INFINITE); + CloseHandle(g_hGETStartedEvent); + g_hGETStartedEvent = NULL; + } + + TaskLock_AcquireExclusive(); +#ifndef ONELOCKTORULETHEMALL + StatsLock_AcquireExclusive(); +#endif + g_FilesTotal = 0; // This causes the Task thread to exit the transfer loop + if (g_hThread) + { + TRACE(_T("InetBgDl: waiting on g_hThread\n")); + if (WAIT_OBJECT_0 != WaitForSingleObject(g_hThread, 10 * 1000)) + { + TRACE(_T("InetBgDl: terminating g_hThread\n")); + TerminateThread(g_hThread, ERROR_OPERATION_ABORTED); + } + CloseHandle(g_hThread); + g_hThread = NULL; + } + g_FilesTotal = 0; + g_FilesCompleted = 0; + g_Status = STATUS_INITIAL; +#ifndef ONELOCKTORULETHEMALL + StatsLock_ReleaseExclusive(); +#endif + for (NSIS::stack_t*pTmpTast,*pTask = g_pLocations; pTask ;) + { + pTmpTast = pTask; + pTask = pTask->next; + StackFreeItem(pTmpTast); + } + g_pLocations = NULL; + TaskLock_ReleaseExclusive(); +} + +UINT_PTR __cdecl NSISPluginCallback(UINT Event) +{ + switch(Event) + { + case NSPIM_UNLOAD: + Reset(); + break; + } + return NULL; +} + +void __stdcall InetStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, + DWORD dwInternetStatus, + LPVOID lpvStatusInformation, + DWORD dwStatusInformationLength) +{ + if (dwInternetStatus == INTERNET_STATUS_NAME_RESOLVED) { + // The documentation states the IP address is a PCTSTR but it is actually a + // PCSTR. + StatsLock_AcquireExclusive(); + wsprintf(g_ServerIP, _T("%S"), lpvStatusInformation); + StatsLock_ReleaseExclusive(); + } + +#if defined(PLUGIN_DEBUG) + switch (dwInternetStatus) + { + case INTERNET_STATUS_RESOLVING_NAME: + TRACE(_T("InetBgDl: INTERNET_STATUS_RESOLVING_NAME (%d), name=%s\n"), + dwStatusInformationLength, lpvStatusInformation); + break; + case INTERNET_STATUS_NAME_RESOLVED: + TRACE(_T("InetBgDl: INTERNET_STATUS_NAME_RESOLVED (%d), resolved name=%s\n"), + dwStatusInformationLength, g_ServerIP); + break; + case INTERNET_STATUS_CONNECTING_TO_SERVER: + TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTING_TO_SERVER (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_CONNECTED_TO_SERVER: + TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTED_TO_SERVER (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_SENDING_REQUEST: + TRACE(_T("InetBgDl: INTERNET_STATUS_SENDING_REQUEST (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_REQUEST_SENT: + TRACE(_T("InetBgDl: INTERNET_STATUS_REQUEST_SENT (%d), bytes sent=%d\n"), + dwStatusInformationLength, lpvStatusInformation); + break; + case INTERNET_STATUS_RECEIVING_RESPONSE: + TRACE(_T("InetBgDl: INTERNET_STATUS_RECEIVING_RESPONSE (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_RESPONSE_RECEIVED: + TRACE(_T("InetBgDl: INTERNET_STATUS_RESPONSE_RECEIVED (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_CTL_RESPONSE_RECEIVED: + TRACE(_T("InetBgDl: INTERNET_STATUS_CTL_RESPONSE_RECEIVED (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_PREFETCH: + TRACE(_T("InetBgDl: INTERNET_STATUS_PREFETCH (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_CLOSING_CONNECTION: + TRACE(_T("InetBgDl: INTERNET_STATUS_CLOSING_CONNECTION (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_CONNECTION_CLOSED: + TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTION_CLOSED (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_HANDLE_CREATED: + TRACE(_T("InetBgDl: INTERNET_STATUS_HANDLE_CREATED (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_HANDLE_CLOSING: + TRACE(_T("InetBgDl: INTERNET_STATUS_HANDLE_CLOSING (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_DETECTING_PROXY: + TRACE(_T("InetBgDl: INTERNET_STATUS_DETECTING_PROXY (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_REQUEST_COMPLETE: + TRACE(_T("InetBgDl: INTERNET_STATUS_REQUEST_COMPLETE (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_REDIRECT: + TRACE(_T("InetBgDl: INTERNET_STATUS_REDIRECT (%d), new url=%s\n"), + dwStatusInformationLength, lpvStatusInformation); + break; + case INTERNET_STATUS_INTERMEDIATE_RESPONSE: + TRACE(_T("InetBgDl: INTERNET_STATUS_INTERMEDIATE_RESPONSE (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_USER_INPUT_REQUIRED: + TRACE(_T("InetBgDl: INTERNET_STATUS_USER_INPUT_REQUIRED (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_STATE_CHANGE: + TRACE(_T("InetBgDl: INTERNET_STATUS_STATE_CHANGE (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_COOKIE_SENT: + TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_SENT (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_COOKIE_RECEIVED: + TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_RECEIVED (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_PRIVACY_IMPACTED: + TRACE(_T("InetBgDl: INTERNET_STATUS_PRIVACY_IMPACTED (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_P3P_HEADER: + TRACE(_T("InetBgDl: INTERNET_STATUS_P3P_HEADER (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_P3P_POLICYREF: + TRACE(_T("InetBgDl: INTERNET_STATUS_P3P_POLICYREF (%d)\n"), + dwStatusInformationLength); + break; + case INTERNET_STATUS_COOKIE_HISTORY: + TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_HISTORY (%d)\n"), + dwStatusInformationLength); + break; + default: + TRACE(_T("InetBgDl: Unknown Status %d\n"), dwInternetStatus); + break; + } +#endif +} + +DWORD CALLBACK TaskThreadProc(LPVOID ThreadParam) +{ + NSIS::stack_t *pURL,*pFile; + HINTERNET hInetSes = NULL, hInetFile = NULL; + DWORD cbio = sizeof(DWORD); + HANDLE hLocalFile; + bool completedFile = false; +startnexttask: + hLocalFile = INVALID_HANDLE_VALUE; + pFile = NULL; + TaskLock_AcquireExclusive(); + // Now that we've acquired the lock, we can set the event to indicate this. + // SetEvent will likely never fail, but if it does we should set it to NULL + // to avoid anyone waiting on it. + if (!SetEvent(g_hGETStartedEvent)) { + CloseHandle(g_hGETStartedEvent); + g_hGETStartedEvent = NULL; + } + pURL = g_pLocations; + if (pURL) + { + pFile = pURL->next; + g_pLocations = pFile->next; + } +#ifndef ONELOCKTORULETHEMALL + StatsLock_AcquireExclusive(); +#endif + if (completedFile) + { + ++g_FilesCompleted; + } + completedFile = false; + g_cbCurrXF = 0; + g_cbCurrTot = FILESIZE_UNKNOWN; + if (!pURL) + { + if (g_FilesTotal) + { + if (g_FilesTotal == g_FilesCompleted) + { + g_Status = STATUS_COMPLETEDALL; + } + } + g_hThread = NULL; + } +#ifndef ONELOCKTORULETHEMALL + StatsLock_ReleaseExclusive(); +#endif + TaskLock_ReleaseExclusive(); + + if (!pURL) + { + if (0) + { +diegle: + DWORD gle = GetLastError(); + //TODO? if (ERROR_INTERNET_EXTENDED_ERROR==gle) InternetGetLastResponseInfo(...) + g_Status = STATUS_ERR_GETLASTERROR; + } + if (hInetSes) + { + InternetCloseHandle(hInetSes); + } + if (INVALID_HANDLE_VALUE != hLocalFile) + { + CloseHandle(hLocalFile); + } + StackFreeItem(pURL); + StackFreeItem(pFile); + return 0; + } + + if (!hInetSes) + { + hInetSes = InternetOpen(USERAGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + if (!hInetSes) + { + TRACE(_T("InetBgDl: InternetOpen failed with gle=%u\n"), + GetLastError()); + goto diegle; + } + InternetSetStatusCallback(hInetSes, (INTERNET_STATUS_CALLBACK)InetStatusCallback); + + //msdn.microsoft.com/library/default.asp?url=/workshop/components/offline/offline.asp#Supporting Offline Browsing in Applications and Components + ULONG longOpt; + DWORD cbio = sizeof(ULONG); + if (InternetQueryOption(hInetSes, INTERNET_OPTION_CONNECTED_STATE, &longOpt, &cbio)) + { + if (INTERNET_STATE_DISCONNECTED_BY_USER&longOpt) + { + INTERNET_CONNECTED_INFO ci = {INTERNET_STATE_CONNECTED, 0}; + InternetSetOption(hInetSes, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci)); + } + } + + // Change the default connect timeout if specified. + if(g_ConnectTimeout > 0) + { + InternetSetOption(hInetSes, INTERNET_OPTION_CONNECT_TIMEOUT, + &g_ConnectTimeout, sizeof(g_ConnectTimeout)); + } + + // Change the default receive timeout if specified. + if (g_ReceiveTimeout) + { + InternetSetOption(hInetSes, INTERNET_OPTION_RECEIVE_TIMEOUT, + &g_ReceiveTimeout, sizeof(DWORD)); + } + } + + DWORD ec = ERROR_SUCCESS; + hLocalFile = CreateFile(pFile->text, GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_DELETE,NULL,CREATE_ALWAYS, 0, NULL); + if (INVALID_HANDLE_VALUE == hLocalFile) + { + TRACE(_T("InetBgDl: CreateFile file handle invalid\n")); + goto diegle; + } + + const DWORD IOURedirFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | + INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; + const DWORD IOUCacheFlags = INTERNET_FLAG_RESYNCHRONIZE | + INTERNET_FLAG_NO_CACHE_WRITE | + INTERNET_FLAG_PRAGMA_NOCACHE | + INTERNET_FLAG_RELOAD; + const DWORD IOUCookieFlags = INTERNET_FLAG_NO_COOKIES; + DWORD IOUFlags = IOURedirFlags | IOUCacheFlags | IOUCookieFlags | + INTERNET_FLAG_NO_UI | INTERNET_FLAG_EXISTING_CONNECT; + + TCHAR *hostname = (TCHAR*) GlobalAlloc(GPTR, MAX_STRLEN * sizeof(TCHAR)), + *urlpath = (TCHAR*) GlobalAlloc(GPTR, MAX_STRLEN * sizeof(TCHAR)), + *extrainfo = (TCHAR*) GlobalAlloc(GPTR, MAX_STRLEN * sizeof(TCHAR)); + + URL_COMPONENTS uc = { sizeof(URL_COMPONENTS), NULL, 0, (INTERNET_SCHEME)0, + hostname, MAX_STRLEN, (INTERNET_PORT)0, NULL, 0, + NULL, 0, urlpath, MAX_STRLEN, extrainfo, MAX_STRLEN}; + uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = MAX_STRLEN; + + if (!InternetCrackUrl(pURL->text, 0, ICU_ESCAPE, &uc)) + { + // Bad url or param passed in + TRACE(_T("InetBgDl: InternetCrackUrl false with url=%s, gle=%u\n"), + pURL->text, GetLastError()); + goto diegle; + } + + TRACE(_T("InetBgDl: scheme_id=%d, hostname=%s, port=%d, urlpath=%s, extrainfo=%s\n"), + uc.nScheme, hostname, uc.nPort, urlpath, extrainfo); + + // Only http and https are supported + if (uc.nScheme != INTERNET_SCHEME_HTTP && + uc.nScheme != INTERNET_SCHEME_HTTPS) + { + TRACE(_T("InetBgDl: only http and https is supported, aborting...\n")); + goto diegle; + } + + TRACE(_T("InetBgDl: calling InternetOpenUrl with url=%s\n"), pURL->text); + hInetFile = InternetOpenUrl(hInetSes, pURL->text, + NULL, 0, IOUFlags | + (uc.nScheme == INTERNET_SCHEME_HTTPS ? + INTERNET_FLAG_SECURE : 0), 1); + if (!hInetFile) + { + TRACE(_T("InetBgDl: InternetOpenUrl failed with gle=%u\n"), + GetLastError()); + goto diegle; + } + + // Get the file length via the Content-Length header + FILESIZE_T cbThisFile; + cbio = sizeof(cbThisFile); + if (!HttpQueryInfo(hInetFile, + HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, + &cbThisFile, &cbio, NULL)) + { + cbThisFile = FILESIZE_UNKNOWN; + } + TRACE(_T("InetBgDl: file size=%d bytes\n"), cbThisFile); + + // Setup a buffer of size 256KiB to store the downloaded data. + const UINT cbBufXF = 262144; + // Use a 4MiB read buffer for the connection. + // Bigger buffers will be faster. + // cbReadBufXF should be a multiple of cbBufXF. + const UINT cbReadBufXF = 4194304; + BYTE bufXF[cbBufXF]; + + // Up the default internal buffer size from 4096 to internalReadBufferSize. + DWORD internalReadBufferSize = cbReadBufXF; + if (!InternetSetOption(hInetFile, INTERNET_OPTION_READ_BUFFER_SIZE, + &internalReadBufferSize, sizeof(DWORD))) + { + TRACE(_T("InetBgDl: InternetSetOption failed to set read buffer size to %u bytes, gle=%u\n"), + internalReadBufferSize, GetLastError()); + + // Maybe it's too big, try half of the optimal value. If that fails just + // use the default. + internalReadBufferSize /= 2; + if (!InternetSetOption(hInetFile, INTERNET_OPTION_READ_BUFFER_SIZE, + &internalReadBufferSize, sizeof(DWORD))) + { + TRACE(_T("InetBgDl: InternetSetOption failed to set read buffer size ") \ + _T("to %u bytes (using default read buffer size), gle=%u\n"), + internalReadBufferSize, GetLastError()); + } + } + + for(;;) + { + DWORD cbio = 0, cbXF = 0; + BOOL retXF = InternetReadFile(hInetFile, bufXF, cbBufXF, &cbio); + if (!retXF) + { + ec = GetLastError(); + TRACE(_T("InetBgDl: InternetReadFile failed, gle=%u\n"), ec); + break; + } + + if (0 == cbio) + { + ASSERT(ERROR_SUCCESS == ec); + // EOF or broken connection? + // TODO: Can InternetQueryDataAvailable detect this? + + TRACE(_T("InetBgDl: InternetReadFile true with 0 cbio, cbThisFile=%d, gle=%u\n"), + cbThisFile, GetLastError()); + // If we haven't transferred all of the file, and we know how big the file + // is, and we have no more data to read from the HTTP request, then set a + // broken pipe error. Reading without StatsLock is ok in this thread. + if (FILESIZE_UNKNOWN != cbThisFile && g_cbCurrXF != cbThisFile) + { + TRACE(_T("InetBgDl: downloaded file size of %d bytes doesn't equal ") \ + _T("expected file size of %d bytes\n"), g_cbCurrXF, cbThisFile); + ec = ERROR_BROKEN_PIPE; + } + break; + } + + // Check if we canceled the download + if (0 == g_FilesTotal) + { + TRACE(_T("InetBgDl: 0 == g_FilesTotal, aborting transfer loop...\n")); + ec = ERROR_CANCELLED; + break; + } + + cbXF = cbio; + if (cbXF) + { + retXF = WriteFile(hLocalFile, bufXF, cbXF, &cbio, NULL); + if (!retXF || cbXF != cbio) + { + ec = GetLastError(); + break; + } + + StatsLock_AcquireExclusive(); + if (FILESIZE_UNKNOWN != cbThisFile) { + g_cbCurrTot = cbThisFile; + } + g_cbCurrXF += cbXF; + StatsLock_ReleaseExclusive(); + } + } + + TRACE(_T("InetBgDl: TaskThreadProc completed %s, ec=%u\n"), pURL->text, ec); + InternetCloseHandle(hInetFile); + if (ERROR_SUCCESS == ec) + { + if (INVALID_HANDLE_VALUE != hLocalFile) + { + CloseHandle(hLocalFile); + hLocalFile = INVALID_HANDLE_VALUE; + } + StackFreeItem(pURL); + StackFreeItem(pFile); + ++completedFile; + } + else + { + TRACE(_T("InetBgDl: failed with ec=%u\n"), ec); + SetLastError(ec); + goto diegle; + } + goto startnexttask; +} + +NSISPIEXPORTFUNC Get(HWND hwndNSIS, UINT N_CCH, TCHAR*N_Vars, NSIS::stack_t**ppST, NSIS::xparams_t*pX) +{ + pX->RegisterPluginCallback(g_hInst, NSISPluginCallback); + for (;;) + { + NSIS::stack_t*pURL = StackPopItem(ppST); + if (!pURL) + { + break; + } + + if (lstrcmpi(pURL->text, _T("/connecttimeout")) == 0) + { + NSIS::stack_t*pConnectTimeout = StackPopItem(ppST); + g_ConnectTimeout = _tcstol(pConnectTimeout->text, NULL, 10) * 1000; + continue; + } + else if (lstrcmpi(pURL->text, _T("/receivetimeout")) == 0) + { + NSIS::stack_t*pReceiveTimeout = StackPopItem(ppST); + g_ReceiveTimeout = _tcstol(pReceiveTimeout->text, NULL, 10) * 1000; + continue; + } + else if (lstrcmpi(pURL->text, _T("/reset")) == 0) + { + StackFreeItem(pURL); + Reset(); + continue; + } + else if (lstrcmpi(pURL->text, _T("/end")) == 0) + { +freeurlandexit: + StackFreeItem(pURL); + break; + } + + NSIS::stack_t*pFile = StackPopItem(ppST); + if (!pFile) + { + goto freeurlandexit; + } + + TaskLock_AcquireExclusive(); + + pFile->next = NULL; + pURL->next = pFile; + NSIS::stack_t*pTasksTail = g_pLocations; + while(pTasksTail && pTasksTail->next) pTasksTail = pTasksTail->next; + if (pTasksTail) + { + pTasksTail->next = pURL; + } + else + { + g_pLocations = pURL; + } + + if (!g_hThread) + { + DWORD tid; + if (g_hGETStartedEvent) { + CloseHandle(g_hGETStartedEvent); + } + g_hGETStartedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + g_hThread = CreateThread(NULL, 0, TaskThreadProc, NULL, 0, &tid); + } + + if (!g_hThread) + { + goto freeurlandexit; + } + +#ifndef ONELOCKTORULETHEMALL + StatsLock_AcquireExclusive(); +#endif + ++g_FilesTotal; +#ifndef ONELOCKTORULETHEMALL + StatsLock_ReleaseExclusive(); +#endif + TaskLock_ReleaseExclusive(); + } +} + +NSISPIEXPORTFUNC GetStats(HWND hwndNSIS, UINT N_CCH, TCHAR*N_Vars, NSIS::stack_t**ppST, NSIS::xparams_t*pX) +{ + NSISPI_INITGLOBALS(N_CCH, N_Vars); + StatsLock_AcquireShared(); + NSIS_SetRegUINT(0, g_Status); + NSIS_SetRegUINT(1, g_FilesCompleted); + NSIS_SetRegUINT(2, g_FilesTotal - g_FilesCompleted); + NSIS_SetRegUINT(3, g_cbCurrXF); + NSIS_SetRegStrEmpty(4); + if (FILESIZE_UNKNOWN != g_cbCurrTot) + { + NSIS_SetRegUINT(4, g_cbCurrTot); + } + NSIS_SetRegStr(5, g_ServerIP); + StatsLock_ReleaseShared(); +} + +EXTERN_C BOOL WINAPI _DllMainCRTStartup(HMODULE hInst, UINT Reason, LPVOID pCtx) +{ + if (DLL_PROCESS_ATTACH==Reason) + { + g_hInst=hInst; + InitializeCriticalSection(&g_CritLock); + } + return TRUE; +} + +BOOL WINAPI DllMain(HINSTANCE hInst, ULONG Reason, LPVOID pCtx) +{ + return _DllMainCRTStartup(hInst, Reason, pCtx); +} + +// For some reason VC6++ doesn't like wcsicmp and swprintf. +// If you use them, you get a linking error about _main +// as an unresolved external. +int main(int argc, char**argv) +{ + return 0; +} diff --git a/other-licenses/nsis/Contrib/InetBgDL/InetBgDL.h b/other-licenses/nsis/Contrib/InetBgDL/InetBgDL.h index d2c1bdd2501..c7fabec2bbf 100644 --- a/other-licenses/nsis/Contrib/InetBgDL/InetBgDL.h +++ b/other-licenses/nsis/Contrib/InetBgDL/InetBgDL.h @@ -1,58 +1,65 @@ -// -// Copyright (C) Anders Kjersem. Licensed under the zlib/libpng license -// - -#if (defined(_MSC_VER) && !defined(_DEBUG)) - #pragma comment(linker,"/opt:nowin98") - #pragma comment(linker,"/ignore:4078") - #pragma comment(linker,"/merge:.rdata=.text") -#endif - -#ifdef UNICODE -# ifndef _UNICODE -# define _UNICODE -# endif -#endif - -#define _WIN32_WINNT 0x0400 -#include -#include -#include - -#if defined(_DEBUG) || 0 -# define PLUGIN_DEBUG 1 -# define TRACE2(fmt,p1,p2) do {TCHAR b[666];wsprintf(b,fmt,p1,p2);OutputDebugString(b);}while(0) -# define TRACEA OutputDebugStringA -#else -# define TRACE2(fmt,p1,p2) -# define TRACEA(fmt) -#endif -#define TRACE1(fmt,p1) TRACE2(fmt,p1,0) - -#ifndef ASSERT -# define ASSERT(x) -#endif - -#define NSISPIEXPORTFUNC EXTERN_C void __declspec(dllexport) __cdecl - -namespace NSIS { - -#define NSISCALL __stdcall -typedef struct _xparams_t { - LPVOID xx1;//exec_flags_type *exec_flags; - int (NSISCALL *ExecuteCodeSegment)(int, HWND); - void (NSISCALL *validate_filename)(TCHAR*); - int (NSISCALL *RegisterPluginCallback)(HMODULE,LPVOID); -} xparams_t; -typedef struct _stack_t { - struct _stack_t *next; - TCHAR text[1]; -} stack_t; - -} // namespace NSIS - -enum NSPIM -{ - NSPIM_UNLOAD, - NSPIM_GUIUNLOAD, -}; +// +// Copyright (C) Anders Kjersem. Licensed under the zlib/libpng license +// + +#if (defined(_MSC_VER) && !defined(_DEBUG)) + #pragma comment(linker,"/opt:nowin98") + #pragma comment(linker,"/ignore:4078") + #pragma comment(linker,"/merge:.rdata=.text") +#endif + +#ifdef UNICODE +# ifndef _UNICODE +# define _UNICODE +# endif +#endif + +#define _WIN32_WINNT 0x0400 +#include +#include +#include + +#if defined(_DEBUG) || 0 +# define PLUGIN_DEBUG 1 +void MYTRACE(LPCTSTR fmt, ...) +{ + va_list argptr; + va_start(argptr, fmt); + TCHAR buffer[2048] = { _T('\0') }; + wvsprintf(buffer, fmt, argptr); + buffer[(sizeof(buffer)/sizeof(*buffer)) - 1] = _T('\0'); + OutputDebugString(buffer); + va_end(argptr); +} +#else +void MYTRACE(...) { } +#endif +# define TRACE MYTRACE + +#ifndef ASSERT +# define ASSERT(x) +#endif + +#define NSISPIEXPORTFUNC EXTERN_C void __declspec(dllexport) __cdecl + +namespace NSIS { + +#define NSISCALL __stdcall +typedef struct _xparams_t { + LPVOID xx1;//exec_flags_type *exec_flags; + int (NSISCALL *ExecuteCodeSegment)(int, HWND); + void (NSISCALL *validate_filename)(TCHAR*); + int (NSISCALL *RegisterPluginCallback)(HMODULE,LPVOID); +} xparams_t; +typedef struct _stack_t { + struct _stack_t *next; + TCHAR text[1]; +} stack_t; + +} // namespace NSIS + +enum NSPIM +{ + NSPIM_UNLOAD, + NSPIM_GUIUNLOAD, +}; diff --git a/other-licenses/nsis/Plugins/InetBgDL.dll b/other-licenses/nsis/Plugins/InetBgDL.dll index 3af9e998bb1..7d5b793cb5c 100644 Binary files a/other-licenses/nsis/Plugins/InetBgDL.dll and b/other-licenses/nsis/Plugins/InetBgDL.dll differ