summaryrefslogtreecommitdiffstats
path: root/other-licenses/nsis/Contrib/BitsUtils
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp317
-rw-r--r--other-licenses/nsis/Contrib/BitsUtils/BitsUtils.sln22
-rw-r--r--other-licenses/nsis/Contrib/BitsUtils/BitsUtils.vcxproj62
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>