feat(GUI): implement Windows elevation using a native module (#1366)

Sentry error reports showcase that elevation errors on Windows are one
of the most frequent Windows errors.

In order to perform Windows elevation, we ship compiled EXEs of a third
party CLI elevation application (http://code.kliu.org/misc/elevate/)
that has several limitations:

- We have the scan the output of the script to determine if a user
  cancelled the elevation request, which causes all sorts of issues on
  computers where English is not the main language

- The application displays a `cmd.exe` window for some milliseconds,
  which is bad UX, that we have to workaround by distributing a patched
  version of the tool

- The CLI application has to be spawned, which seems to be problematic
  if users have anti-virus software, leading to hard to debug issues

- We don't have any control if something goes wrong

For these reasons, we decided to implement our own elevation mechanism
in C++ as a Node.js add-on, based on the `elevate.exe` code we where
previously using.

Misc changes:

- Introduce a `lib/shared/bindings.js` module to easily require local native
  add-ons

- Install `cpplint` and configure it to lint C++ files

Note that for practical reasons, the C++ code lives in this repository
rather than in a separate module. We will release this functionality in
a more accessible way in the future as part of the Etcher SDK project.

Change-Type: patch
Changelog-Entry: Fix uncaught errors when cancelling elevation requests on Windows when the system's language is not English.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit is contained in:
Juan Cruz Viotti 2017-05-02 18:36:57 -04:00 committed by GitHub
parent 9881364e1d
commit b75dfd3ece
19 changed files with 482 additions and 43 deletions

3
.gitattributes vendored
View File

@ -13,6 +13,9 @@ etcher text
.git* text
*.html text
*.json text
*.cpp text
*.h text
*.gyp text
LICENSE text
Makefile text
*.md text

2
.gitignore vendored
View File

@ -21,7 +21,7 @@ coverage
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
build
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git

View File

@ -40,7 +40,7 @@ install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
./scripts/build/docker/run-command.sh -r "${TARGET_ARCH}" -s "${PWD}" -c "make info && make electron-develop";
else
pip install codespell==1.9.2 awscli;
pip install codespell==1.9.2 awscli cpplint;
brew install afsctool jq;
make info;
travis_wait make electron-develop;

View File

@ -174,40 +174,40 @@ $(BUILD_DIRECTORY):
$(BUILD_TEMPORARY_DIRECTORY): | $(BUILD_DIRECTORY)
mkdir $@
$(BUILD_DIRECTORY)/electron-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies: | $(BUILD_DIRECTORY)
mkdir $@
$(BUILD_DIRECTORY)/node-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies: | $(BUILD_DIRECTORY)
mkdir $@
$(BUILD_OUTPUT_DIRECTORY): | $(BUILD_DIRECTORY)
mkdir $@
$(BUILD_DIRECTORY)/electron-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies/node_modules: package.json npm-shrinkwrap.json \
| $(BUILD_DIRECTORY)/electron-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies
$(BUILD_DIRECTORY)/electron-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies: package.json npm-shrinkwrap.json \
| $(BUILD_DIRECTORY)
mkdir $@
cp -rf src $@
./scripts/build/dependencies-npm.sh -p \
-r "$(TARGET_ARCH)" \
-v "$(ELECTRON_VERSION)" \
-x $| \
-x $@ \
-t electron \
-s "$(TARGET_PLATFORM)"
rm -rf $@/src
$(BUILD_DIRECTORY)/node-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies/node_modules: package.json npm-shrinkwrap.json \
| $(BUILD_DIRECTORY)/node-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies
$(BUILD_DIRECTORY)/node-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies: package.json npm-shrinkwrap.json \
| $(BUILD_DIRECTORY)
mkdir $@
cp -rf src $@
./scripts/build/dependencies-npm.sh -p -f \
-r "$(TARGET_ARCH)" \
-v "$(NODE_VERSION)" \
-x $| \
-x $@ \
-t node \
-s "$(TARGET_PLATFORM)"
rm -rf $@/src
$(BUILD_DIRECTORY)/electron-$(TARGET_PLATFORM)-$(APPLICATION_VERSION)-$(TARGET_ARCH)-app: \
$(BUILD_DIRECTORY)/electron-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies/node_modules \
$(BUILD_DIRECTORY)/electron-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies \
| $(BUILD_DIRECTORY) $(BUILD_TEMPORARY_DIRECTORY)
./scripts/build/electron-create-resources-app.sh -s . -o $@ \
-v $(APPLICATION_VERSION) \
-f "$(APPLICATION_FILES)"
cp -RLf $< $@
cp -RLf $</* $@
ifdef ANALYTICS_SENTRY_TOKEN
./scripts/build/jq-insert.sh \
@ -240,10 +240,12 @@ $(BUILD_DIRECTORY)/electron-$(ELECTRON_VERSION)-$(TARGET_PLATFORM)-$(TARGET_ARCH
$(BUILD_DIRECTORY)/$(APPLICATION_NAME)-cli-$(TARGET_PLATFORM)-$(APPLICATION_VERSION)-$(TARGET_ARCH)-app: \
package.json lib \
$(BUILD_DIRECTORY)/node-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies/node_modules \
$(BUILD_DIRECTORY)/node-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies \
| $(BUILD_DIRECTORY)
mkdir $@
$(foreach prerequisite,$^,$(call execute-command,cp -rf $(prerequisite) $@))
cp $(word 1,$^) $@
cp $(word 2,$^) $@
cp -rf $(word 3,$^)/* $@
$(BUILD_DIRECTORY)/$(APPLICATION_NAME)-cli-$(TARGET_PLATFORM)-$(APPLICATION_VERSION)-$(TARGET_ARCH).js: \
$(BUILD_DIRECTORY)/$(APPLICATION_NAME)-cli-$(TARGET_PLATFORM)-$(APPLICATION_VERSION)-$(TARGET_ARCH)-app \
@ -251,10 +253,10 @@ $(BUILD_DIRECTORY)/$(APPLICATION_NAME)-cli-$(TARGET_PLATFORM)-$(APPLICATION_VERS
./scripts/build/concatenate-javascript.sh -e $</lib/cli/etcher.js -o $@
$(BUILD_DIRECTORY)/$(APPLICATION_NAME)-cli-$(APPLICATION_VERSION)-$(TARGET_PLATFORM)-$(TARGET_ARCH): \
$(BUILD_DIRECTORY)/node-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies/node_modules \
$(BUILD_DIRECTORY)/node-$(TARGET_PLATFORM)-$(TARGET_ARCH)-dependencies \
$(BUILD_DIRECTORY)/$(APPLICATION_NAME)-cli-$(TARGET_PLATFORM)-$(APPLICATION_VERSION)-$(TARGET_ARCH).js \
| $(BUILD_DIRECTORY) $(BUILD_TEMPORARY_DIRECTORY)
./scripts/build/node-package-cli.sh -o $@ -l $< \
./scripts/build/node-package-cli.sh -o $@ -l $</node_modules \
-n $(APPLICATION_NAME) \
-e $(word 2,$^) \
-r $(TARGET_ARCH) \

View File

@ -32,7 +32,7 @@ install:
- set PATH=C:\Program Files (x86)\NSIS;%PATH%
- set PATH=C:\MinGW\bin;%PATH%
- set PATH=C:\MinGW\msys\1.0\bin;%PATH%
- pip install codespell==1.9.2 awscli
- pip install codespell==1.9.2 awscli cpplint
- make info
- make electron-develop

25
binding.gyp Normal file
View File

@ -0,0 +1,25 @@
{
"targets": [
{
"target_name": "elevator",
"include_dirs" : [
"src",
"<!(node -e \"require('nan')\")"
],
'conditions': [
[ 'OS=="win"', {
"sources": [
"src/utils/v8utils.cpp",
"src/os/elevate.cpp",
"src/elevator_init.cpp",
],
"libraries": [
"-lShell32.lib",
],
} ]
],
}
],
}

View File

@ -14,6 +14,7 @@ Prerequisites
- [jq](https://stedolan.github.io/jq/)
- [Codespell](https://github.com/lucasdemarchi/codespell)
- [curl](https://curl.haxx.se/)
- [cpplint](https://github.com/cpplint/cpplint)
### Windows

44
lib/shared/bindings.js Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright 2017 resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const path = require('path');
const bindings = require('bindings');
/**
* @summary Load a native module
* @function
* @public
*
* @param {String} moduleName - native module name
* @returns {Object} native module
*
* @example
* const elevator = bindings.load('elevator');
*/
exports.load = (moduleName) => {
return bindings({
bindings: moduleName,
/* eslint-disable camelcase */
module_root: path.join(__dirname, '..', '..')
/* eslint-enable camelcase */
});
};

View File

@ -17,6 +17,7 @@
'use strict';
const os = require('os');
const bindings = require('./bindings');
const Bluebird = require('bluebird');
const childProcess = Bluebird.promisifyAll(require('child_process'));
const sudoPrompt = Bluebird.promisifyAll(require('sudo-prompt'));
@ -145,22 +146,13 @@ exports.elevateCommand = (command, options) => {
const prefixedCommand = _.concat(exports.getEnvironmentCommandPrefix(options.environment), command);
if (os.platform() === 'win32') {
const elevator = Bluebird.promisifyAll(require('elevator'));
return elevator.executeAsync(prefixedCommand, {
hidden: true,
terminating: true,
doNotPushdCurrentDirectory: true,
waitForTermination: true
}).then(() => {
const elevator = Bluebird.promisifyAll(bindings.load('elevator'));
return elevator.elevateAsync(_.concat([
'cmd.exe',
'/c'
], prefixedCommand)).then((results) => {
return {
cancelled: false
};
}).catch({
code: 'ELEVATE_CANCELLED'
}, () => {
return {
cancelled: true
cancelled: results.cancelled
};
});
}

View File

@ -9,6 +9,7 @@
"description": "Flash OS images to SD cards & USB drives, safely and easily.",
"productDescription": "Etcher is a powerful OS image flasher built with web technologies to ensure flashing an SDCard or USB drive is a pleasant and safe experience. It protects you from accidentally writing to your hard-drives, ensures every byte of data was written correctly and much more.",
"homepage": "https://github.com/resin-io/etcher",
"gypfile": true,
"repository": {
"type": "git",
"url": "git@github.com:resin-io/etcher.git"
@ -16,14 +17,18 @@
"scripts": {
"test": "npm run lint && electron-mocha --recursive --renderer tests/gui -R spec && electron-mocha --recursive tests/shared tests/child-writer tests/image-stream -R spec",
"sass": "node-sass ./lib/gui/scss/main.scss > ./lib/gui/css/main.css",
"cpplint": "cpplint --recursive src",
"jslint": "eslint lib tests scripts bin versionist.conf.js",
"sasslint": "sass-lint lib/gui/scss",
"htmllint": "node scripts/html-lint.js",
"codespell": "codespell.py --skip *.gz,*.bz2,*.xz,*.zip,*.img,*.dmg,*.iso,.DS_Store lib tests docs scripts Makefile *.md LICENSE",
"lint": "npm run jslint && npm run sasslint && npm run codespell && npm run htmllint",
"lint": "npm run jslint && npm run sasslint && npm run cpplint && npm run codespell && npm run htmllint",
"changelog": "versionist",
"start": "electron lib/start.js",
"preshrinkwrap": "node ./scripts/clean-shrinkwrap.js"
"preshrinkwrap": "node ./scripts/clean-shrinkwrap.js",
"configure": "node-gyp configure",
"build": "node-gyp build",
"install": "node-gyp rebuild"
},
"author": "Juan Cruz Viotti <juan@resin.io>",
"license": "Apache-2.0",
@ -61,8 +66,7 @@
}
},
"optionalDependencies": {
"electron-installer-debian": "0.5.1",
"elevator": "2.2.3"
"electron-installer-debian": "0.5.1"
},
"dependencies": {
"angular": "1.6.3",
@ -72,6 +76,7 @@
"angular-seconds-to-date": "^1.0.0",
"angular-ui-bootstrap": "^2.5.0",
"angular-ui-router": "^0.4.2",
"bindings": "^1.2.1",
"bluebird": "^3.0.5",
"bootstrap-sass": "^3.3.5",
"chalk": "^1.1.3",
@ -86,6 +91,7 @@
"lzma-native": "^1.5.2",
"mime-types": "^2.1.15",
"mountutils": "^1.0.6",
"nan": "^2.3.5",
"node-ipc": "^8.9.2",
"node-stream-zip": "^1.3.4",
"path-is-inside": "^1.0.2",
@ -120,6 +126,7 @@
"html-angular-validate": "^0.1.9",
"mochainon": "^1.0.0",
"nock": "^9.0.9",
"node-gyp": "^3.5.0",
"node-sass": "^3.8.0",
"sass-lint": "^1.10.2",
"tmp": "0.0.31",

View File

@ -129,12 +129,17 @@ if [ -n "$ARGV_PREFIX" ]; then
cp "$PWD/npm-shrinkwrap.json" "$ARGV_PREFIX/npm-shrinkwrap.json"
fi
if [ -f "$PWD/binding.gyp" ]; then
cp "$PWD/binding.gyp" "$ARGV_PREFIX/binding.gyp"
fi
pushd "$ARGV_PREFIX"
run_install
popd
rm -f "$ARGV_PREFIX/package.json"
rm -f "$ARGV_PREFIX/npm-shrinkwrap.json"
rm -f "$ARGV_PREFIX/binding.gyp"
else
run_install
fi

View File

@ -36,4 +36,4 @@ RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \
RUN npm config set spin=false
# Python
RUN pip install codespell==1.9.2 awscli
RUN pip install codespell==1.9.2 awscli cpplint

View File

@ -36,4 +36,4 @@ RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \
RUN npm config set spin=false
# Python
RUN pip install codespell==1.9.2 awscli
RUN pip install codespell==1.9.2 awscli cpplint

View File

@ -36,4 +36,4 @@ RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \
RUN npm config set spin=false
# Python
RUN pip install codespell==1.9.2 awscli
RUN pip install codespell==1.9.2 awscli cpplint

61
src/elevator_init.cpp Normal file
View File

@ -0,0 +1,61 @@
/*
* Copyright 2017 resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include <vector>
#include "os/elevate.h"
#include "utils/v8utils.h"
NAN_METHOD(Elevate) {
if (!info[0]->IsArray()) {
return Nan::ThrowError("This function expects an array");
}
if (!info[1]->IsFunction()) {
return Nan::ThrowError("Callback must be a function");
}
std::vector<std::string> arguments =
etcher::v8utils::GetArguments(info[0].As<v8::Array>());
v8::Local<v8::Function> callback = info[1].As<v8::Function>();
etcher::ELEVATE_RESULT result = etcher::Elevate(
arguments.front(),
std::vector<std::string>(arguments.begin() + 1, arguments.end()));
// Create results object
v8::Isolate *isolate = v8::Isolate::GetCurrent();
v8::Local<v8::Object> results = v8::Object::New(isolate);
switch (result) {
case etcher::ELEVATE_RESULT::ELEVATE_SUCCESS:
results->Set(v8::String::NewFromUtf8(isolate, "cancelled"), Nan::False());
YIELD_OBJECT(callback, results);
break;
case etcher::ELEVATE_RESULT::ELEVATE_CANCELLED:
results->Set(v8::String::NewFromUtf8(isolate, "cancelled"), Nan::True());
YIELD_OBJECT(callback, results);
break;
default:
std::string details = etcher::ElevateResultToString(result);
YIELD_ERROR(callback, details.c_str());
}
}
NAN_MODULE_INIT(ElevatorInit) { NAN_SET_FUNCTION("elevate", Elevate); }
NODE_MODULE(elevator, ElevatorInit)

156
src/os/elevate.cpp Normal file
View File

@ -0,0 +1,156 @@
/*
* Copyright 2017 resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "os/elevate.h"
static std::string JoinArguments(std::vector<std::string> arguments) {
std::ostringstream result;
std::copy(arguments.begin(), arguments.end(),
std::ostream_iterator<std::string>(result, " "));
return result.str();
}
// Make sure to delete the result after you're done
// with it by calling `delete[] result;`.
// See http://stackoverflow.com/a/1201471
static LPCTSTR ConvertStringToLPCTSTR(const std::string &string) {
char *result = new char[string.size() + 1];
std::copy(string.begin(), string.end(), result);
result[string.size()] = 0;
return result;
}
etcher::ELEVATE_RESULT etcher::Elevate(const std::string &command,
std::vector<std::string> arguments) {
// Initialize the SHELLEXECUTEINFO structure. We zero it out
// in order to be on the safe side, and set cbSize to the size
// of the structure as recommend by MSDN
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/bb759784(v=vs.85).aspx
SHELLEXECUTEINFO shellExecuteInfo;
ZeroMemory(&shellExecuteInfo, sizeof(shellExecuteInfo));
shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFO);
// Flags that indicate the content and validity of the other structure member.
shellExecuteInfo.fMask =
// Used to indicate that the hProcess member receives the process handle.
// This handle is typically used to allow an application to find out
// when a process created with ShellExecuteEx terminates.
SEE_MASK_NOCLOSEPROCESS |
// Wait for the execute operation to complete before returning.
SEE_MASK_NOASYNC |
// Do not display an error message box if an error occurs.
SEE_MASK_FLAG_NO_UI;
// The action to be performed.
shellExecuteInfo.lpVerb = TEXT("runas");
// Run the file in the background
shellExecuteInfo.nShow = SW_HIDE;
// Use the current directory as the working directory
shellExecuteInfo.lpDirectory = NULL;
// Set file and parameters
// We can't just assign the result of `.c_str()`, since
// that pointer is owned by the `std::string` instance,
// and will not be safe after the instance is destroyed.
LPCTSTR file = ConvertStringToLPCTSTR(command);
LPCTSTR argv = ConvertStringToLPCTSTR(JoinArguments(arguments));
shellExecuteInfo.lpFile = file;
shellExecuteInfo.lpParameters = argv;
BOOL executeResult = ShellExecuteEx(&shellExecuteInfo);
delete[] file;
delete[] argv;
// Finally, let's try to elevate the command
if (!executeResult) {
DWORD executeError = GetLastError();
switch (executeError) {
case ERROR_FILE_NOT_FOUND:
return etcher::ELEVATE_RESULT::ELEVATE_FILE_NOT_FOUND;
case ERROR_PATH_NOT_FOUND:
return etcher::ELEVATE_RESULT::ELEVATE_PATH_NOT_FOUND;
case ERROR_DDE_FAIL:
return etcher::ELEVATE_RESULT::ELEVATE_DDE_FAIL;
case ERROR_NO_ASSOCIATION:
return etcher::ELEVATE_RESULT::ELEVATE_NO_ASSOCIATION;
case ERROR_ACCESS_DENIED:
return etcher::ELEVATE_RESULT::ELEVATE_ACCESS_DENIED;
case ERROR_DLL_NOT_FOUND:
return etcher::ELEVATE_RESULT::ELEVATE_DLL_NOT_FOUND;
case ERROR_CANCELLED:
return etcher::ELEVATE_RESULT::ELEVATE_CANCELLED;
case ERROR_NOT_ENOUGH_MEMORY:
return etcher::ELEVATE_RESULT::ELEVATE_NOT_ENOUGH_MEMORY;
case ERROR_SHARING_VIOLATION:
return etcher::ELEVATE_RESULT::ELEVATE_SHARING_VIOLATION;
default:
return etcher::ELEVATE_RESULT::ELEVATE_UNKNOWN_ERROR;
}
}
// Since we passed SEE_MASK_NOCLOSEPROCESS, the
// process handle is accessible from hProcess.
if (shellExecuteInfo.hProcess) {
// Wait for the process to exit before continuing.
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx
WaitForSingleObject(shellExecuteInfo.hProcess, INFINITE);
if (!CloseHandle(shellExecuteInfo.hProcess)) {
return etcher::ELEVATE_RESULT::ELEVATE_UNKNOWN_ERROR;
}
}
return etcher::ELEVATE_RESULT::ELEVATE_SUCCESS;
}
std::string
etcher::ElevateResultToString(const etcher::ELEVATE_RESULT &result) {
switch (result) {
case etcher::ELEVATE_RESULT::ELEVATE_SUCCESS:
return "Success";
case etcher::ELEVATE_RESULT::ELEVATE_CANCELLED:
return "The user cancelled the elevation request";
case etcher::ELEVATE_RESULT::ELEVATE_FILE_NOT_FOUND:
return "The specified file was not found";
case etcher::ELEVATE_RESULT::ELEVATE_PATH_NOT_FOUND:
return "The specified path was not found";
case etcher::ELEVATE_RESULT::ELEVATE_DDE_FAIL:
return "The Dynamic Data Exchange (DDE) transaction failed";
case etcher::ELEVATE_RESULT::ELEVATE_NO_ASSOCIATION:
return "There is no application associated with the "
"specified file name extension";
case etcher::ELEVATE_RESULT::ELEVATE_ACCESS_DENIED:
return "Access to the specified file is denied";
case etcher::ELEVATE_RESULT::ELEVATE_DLL_NOT_FOUND:
return "One of the library files necessary to run the "
"application can't be found";
case etcher::ELEVATE_RESULT::ELEVATE_NOT_ENOUGH_MEMORY:
return "There is not enough memory to perform the specified action";
case etcher::ELEVATE_RESULT::ELEVATE_SHARING_VIOLATION:
return "A sharing violation occurred";
default:
return "Unknown error";
}
}

59
src/os/elevate.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef SRC_OS_ELEVATE_H_
#define SRC_OS_ELEVATE_H_
/*
* Copyright 2017 resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Fix winsock.h redefinition errors
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
// Note that windows.h has to be included before any
// other Windows library to avoid declaration issues
#include <windows.h>
#include <shellapi.h>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
namespace etcher {
enum class ELEVATE_RESULT {
ELEVATE_SUCCESS,
ELEVATE_FILE_NOT_FOUND,
ELEVATE_PATH_NOT_FOUND,
ELEVATE_DDE_FAIL,
ELEVATE_NO_ASSOCIATION,
ELEVATE_ACCESS_DENIED,
ELEVATE_DLL_NOT_FOUND,
ELEVATE_CANCELLED,
ELEVATE_NOT_ENOUGH_MEMORY,
ELEVATE_SHARING_VIOLATION,
ELEVATE_UNKNOWN_ERROR
};
ELEVATE_RESULT Elevate(const std::string &command,
std::vector<std::string> arguments);
std::string ElevateResultToString(const ELEVATE_RESULT &result);
} // namespace etcher
#endif // SRC_OS_ELEVATE_H_

30
src/utils/v8utils.cpp Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright 2017 resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utils/v8utils.h"
std::vector<std::string>
etcher::v8utils::GetArguments(v8::Local<v8::Array> arguments) {
std::vector<std::string> result(0);
for (uint32_t index = 0; index < arguments->Length(); index++) {
std::string argument(
*v8::String::Utf8Value(arguments->Get(index)->ToString()));
result.push_back(argument);
}
return result;
}

54
src/utils/v8utils.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef SRC_UTILS_V8UTILS_H_
#define SRC_UTILS_V8UTILS_H_
/*
* Copyright 2017 resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nan.h>
#include <string>
#include <vector>
namespace etcher {
namespace v8utils {
std::vector<std::string> GetArguments(v8::Local<v8::Array> arguments);
} // namespace v8utils
} // namespace etcher
#define YIELD_ERROR(CALLBACK, ERROR) \
{ \
v8::Local<v8::Value> argv[1] = {Nan::Error((ERROR))}; \
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), (CALLBACK), 1, \
argv); \
} \
return;
#define YIELD_OBJECT(CALLBACK, OBJECT) \
{ \
v8::Local<v8::Value> argv[2] = {Nan::Null(), (OBJECT)}; \
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), (CALLBACK), 2, \
argv); \
} \
return;
#define YIELD_NOTHING(CALLBACK) \
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), (CALLBACK), 0, 0);
#define NAN_SET_FUNCTION(JSSYMBOL, FUNCTION) \
Nan::Set(target, Nan::New((JSSYMBOL)).ToLocalChecked(), \
Nan::GetFunction(Nan::New<v8::FunctionTemplate>((FUNCTION))) \
.ToLocalChecked());
#endif // SRC_UTILS_V8UTILS_H_