diff options
Diffstat (limited to 'other-licenses/nsis/Contrib/BitsUtils')
-rw-r--r-- | other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp | 317 | ||||
-rw-r--r-- | other-licenses/nsis/Contrib/BitsUtils/BitsUtils.sln | 22 | ||||
-rw-r--r-- | other-licenses/nsis/Contrib/BitsUtils/BitsUtils.vcxproj | 62 |
3 files changed, 401 insertions, 0 deletions
diff --git a/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp b/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp new file mode 100644 index 0000000000..aea1f270ea --- /dev/null +++ b/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <windows.h> +#include <bits.h> +#include <utility> + +// Avoid conversions, we will only build Unicode anyway. +#if !(defined(UNICODE) && defined(_UNICODE)) +# error "Unicode required" +#endif + +static HINSTANCE gHInst; + +// ***** Section: ScopeExit +// Derived from mfbt mozilla::ScopeExit, I have removed the use of +// GuardObjectNotifier and the MOZ_* annotations. +template <typename ExitFunction> +class ScopeExit { + ExitFunction mExitFunction; + bool mExecuteOnDestruction; + + public: + explicit ScopeExit(ExitFunction &&cleanup) + : mExitFunction(cleanup), mExecuteOnDestruction(true) {} + + ScopeExit(ScopeExit &&rhs) + : mExitFunction(std::move(rhs.mExitFunction)), + mExecuteOnDestruction(rhs.mExecuteOnDestruction) { + rhs.release(); + } + + ~ScopeExit() { + if (mExecuteOnDestruction) { + mExitFunction(); + } + } + + void release() { mExecuteOnDestruction = false; } + + private: + explicit ScopeExit(const ScopeExit &) = delete; + ScopeExit &operator=(const ScopeExit &) = delete; + ScopeExit &operator=(ScopeExit &&) = delete; +}; + +template <typename ExitFunction> +ScopeExit<ExitFunction> MakeScopeExit(ExitFunction &&exitFunction) { + return ScopeExit<ExitFunction>(std::move(exitFunction)); +} + +// ***** Section: NSIS stack +typedef struct _stack_t { + struct _stack_t *next; + WCHAR text[1]; // this should be the length of g_stringsize when allocating +} stack_t; + +static unsigned int g_stringsize; +static stack_t **g_stacktop; + +static int popstringn(LPWSTR str, int maxlen) { + stack_t *th; + if (!g_stacktop || !*g_stacktop) return 1; + th = (*g_stacktop); + if (str) lstrcpynW(str, th->text, maxlen ? maxlen : g_stringsize); + *g_stacktop = th->next; + GlobalFree((HGLOBAL)th); + return 0; +} + +static void pushstring(LPCWSTR str) { + stack_t *th; + if (!g_stacktop) return; + th = (stack_t *)GlobalAlloc( + GPTR, (sizeof(stack_t) + (g_stringsize) * sizeof(*str))); + lstrcpynW(th->text, str, g_stringsize); + th->next = *g_stacktop; + *g_stacktop = th; +} + +// ***** Section: NSIS Plug-In API (from NSIS api.h) +// NSIS Plug-In Callback Messages +enum NSPIM { + NSPIM_UNLOAD, // This is the last message a plugin gets, do final cleanup + NSPIM_GUIUNLOAD, // Called after .onGUIEnd +}; + +// Prototype for callbacks registered with +// extra_parameters->RegisterPluginCallback() Return NULL for unknown messages +// Should always be __cdecl for future expansion possibilities +typedef UINT_PTR (*NSISPLUGINCALLBACK)(enum NSPIM); + +#define NSISCALL __stdcall + +typedef struct { + LPVOID exec_flags; + int(NSISCALL *ExecuteCodeSegment)(int, HWND); + void(NSISCALL *validate_filename)(LPWSTR); + int(NSISCALL *RegisterPluginCallback)( + HMODULE, NSISPLUGINCALLBACK); // returns 0 on success, 1 if already + // registered and < 0 on errors +} extra_parameters; + +// ***** Section: StartBitsThread +UINT_PTR __cdecl NSISPluginCallback(NSPIM msg); + +static struct { + HANDLE thread; + bool shutdown_requested; + CRITICAL_SECTION cs; + CONDITION_VARIABLE cv; +} gStartBitsThread = {nullptr, false, 0, 0}; + +// This thread connects to the BackgroundCopyManager, which may take some time +// if the BITS service is not already running. It also holds open the connection +// until gStartBitsThread.shutdown_requested becomes true. +DWORD WINAPI StartBitsThreadProc(LPVOID) { + EnterCriticalSection(&gStartBitsThread.cs); + auto leaveCS = + MakeScopeExit([] { LeaveCriticalSection(&gStartBitsThread.cs); }); + + if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) { + return 0; + } + auto coUninit = MakeScopeExit([] { CoUninitialize(); }); + + IBackgroundCopyManager *bcm = nullptr; + if (FAILED(CoCreateInstance( + __uuidof(BackgroundCopyManager), nullptr, CLSCTX_LOCAL_SERVER, + __uuidof(IBackgroundCopyManager), (LPVOID *)&bcm)) || + !bcm) { + return 0; + } + + do { + SleepConditionVariableCS(&gStartBitsThread.cv, &gStartBitsThread.cs, + INFINITE); + } while (!gStartBitsThread.shutdown_requested); + + bcm->Release(); + return 1; +} + +// Start up the thread +// returns true on success +bool StartBitsServiceBackgroundThreadImpl(extra_parameters *extra_params) { + EnterCriticalSection(&gStartBitsThread.cs); + auto leaveCS = + MakeScopeExit([] { LeaveCriticalSection(&gStartBitsThread.cs); }); + + if (gStartBitsThread.thread) { + // Thread is already started, assumed to be still running. + return true; + } + + // Ensure the callback is registered so the thread can be stopped, and also so + // NSIS doesn't unload this DLL. + extra_params->RegisterPluginCallback(gHInst, NSISPluginCallback); + + gStartBitsThread.shutdown_requested = false; + + gStartBitsThread.thread = + CreateThread(nullptr, 0, StartBitsThreadProc, nullptr, 0, 0); + if (!gStartBitsThread.thread) { + return false; + } + + return true; +} + +// Shut down the Start BITS thread, if it was started. +void ShutdownStartBitsThread() { + EnterCriticalSection(&gStartBitsThread.cs); + if (gStartBitsThread.thread) { + gStartBitsThread.shutdown_requested = true; + WakeAllConditionVariable(&gStartBitsThread.cv); + LeaveCriticalSection(&gStartBitsThread.cs); + + // Give the thread a little time to clean up. + if (WaitForSingleObject(gStartBitsThread.thread, 1000) == WAIT_OBJECT_0) { + EnterCriticalSection(&gStartBitsThread.cs); + gStartBitsThread.thread = nullptr; + LeaveCriticalSection(&gStartBitsThread.cs); + } else { + // Don't attempt to recover if we didn't see the thread end, + // the process will be exiting soon anyway. + } + + } else { + LeaveCriticalSection(&gStartBitsThread.cs); + } +} + +// ***** Section: CancelBitsJobsByName +#define MAX_JOB_NAME 256 + +bool CancelBitsJobsByNameImpl(LPWSTR matchJobName) { + if (FAILED(CoInitialize(nullptr))) { + return false; + } + auto coUninit = MakeScopeExit([] { CoUninitialize(); }); + + IBackgroundCopyManager *bcm = nullptr; + if (FAILED(CoCreateInstance( + __uuidof(BackgroundCopyManager), nullptr, CLSCTX_LOCAL_SERVER, + __uuidof(IBackgroundCopyManager), (LPVOID *)&bcm)) || + !bcm) { + return false; + } + auto bcmRelease = MakeScopeExit([bcm] { bcm->Release(); }); + + IEnumBackgroundCopyJobs *enumerator = nullptr; + // Attempt to enumerate jobs for all users. If that fails, + // try for only the current user. + if (FAILED(bcm->EnumJobs(BG_JOB_ENUM_ALL_USERS, &enumerator))) { + enumerator = nullptr; + if (FAILED(bcm->EnumJobs(0, &enumerator))) { + return false; + } + } + if (!enumerator) { + return false; + } + auto enumeratorRelease = + MakeScopeExit([enumerator] { enumerator->Release(); }); + + bool success = true; + + IBackgroundCopyJob *job = nullptr; + HRESULT nextResult; + while ((nextResult = enumerator->Next(1, &job, nullptr), + SUCCEEDED(nextResult))) { + if (nextResult == S_FALSE) { + break; + } + if (!job) { + success = false; + break; + } + + LPWSTR curJobName = nullptr; + + if (SUCCEEDED(job->GetDisplayName(&curJobName)) && curJobName) { + if (lstrcmpW(curJobName, matchJobName) == 0) { + if (!SUCCEEDED(job->Cancel())) { + // If we can't cancel we can still try the other jobs. + success = false; + } + } + CoTaskMemFree((LPVOID)curJobName); + curJobName = nullptr; + } else { + // We may not be able to access certain jobs, keep trying the rest. + success = false; + } + + job->Release(); + job = nullptr; + } + + if (!SUCCEEDED(nextResult)) { + success = false; + } + + return success; +} + +// ***** Section: DLL entry points +extern "C" { +// Cancel all BITS jobs with the given name. +void __declspec(dllexport) + CancelBitsJobsByName(HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters *) { + g_stacktop = stacktop; + g_stringsize = string_size; + + WCHAR matchJobName[MAX_JOB_NAME + 1]; + matchJobName[0] = L'\0'; + + if (!popstringn(matchJobName, sizeof(matchJobName) / sizeof(WCHAR))) { + if (CancelBitsJobsByNameImpl(matchJobName)) { + pushstring(L"ok"); + return; + } + } + + pushstring(L"error"); +} + +// Start the BITS service in the background, and hold a reference to it until +// the (un)installer exits. +// Does not provide any feedback or touch the stack. +void __declspec(dllexport) + StartBitsServiceBackground(HWND, int, char *, stack_t **, + extra_parameters *extra_params) { + StartBitsServiceBackgroundThreadImpl(extra_params); +} +} + +// Handle messages from NSIS +UINT_PTR __cdecl NSISPluginCallback(NSPIM msg) { + if (msg == NSPIM_UNLOAD) { + ShutdownStartBitsThread(); + } + return 0; +} + +BOOL APIENTRY DllMain(HINSTANCE instance, DWORD reason, LPVOID) { + if (reason == DLL_PROCESS_ATTACH) { + gHInst = instance; + InitializeConditionVariable(&gStartBitsThread.cv); + InitializeCriticalSection(&gStartBitsThread.cs); + } + return TRUE; +} diff --git a/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.sln b/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.sln new file mode 100644 index 0000000000..5e0311a9b2 --- /dev/null +++ b/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.sln @@ -0,0 +1,22 @@ +
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.271
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BitsUtils", "BitsUtils.vcxproj", "{5058AAED-D02A-4F86-B011-31516AB5CD63}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5058AAED-D02A-4F86-B011-31516AB5CD63}.Release|x86.ActiveCfg = Release|Win32
+ {5058AAED-D02A-4F86-B011-31516AB5CD63}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {53DD94C2-CC74-40B7-B9B7-08AD945C28F3}
+ EndGlobalSection
+EndGlobal
diff --git a/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.vcxproj b/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.vcxproj new file mode 100644 index 0000000000..71520de97e --- /dev/null +++ b/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.vcxproj @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{5058AAED-D02A-4F86-B011-31516AB5CD63}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>BitsUtils</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>false</SDLCheck>
+ <PreprocessorDefinitions>WINVER=0x601;_WIN32_WINNT=0x601;WIN32;NDEBUG;CANCELBITSJOBS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <OmitDefaultLibName>true</OmitDefaultLibName>
+ <ExceptionHandling>false</ExceptionHandling>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EntryPointSymbol>DllMain</EntryPointSymbol>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="BitsUtils.cpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
|