1
0
Fork 0
arangodb/lib/V8/v8-environment.cpp

265 lines
8.4 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <string.h>
#include <cstdint>
#include <string>
#include <v8.h>
#include "ApplicationFeatures/ApplicationServer.h"
#include "ApplicationFeatures/V8SecurityFeature.h"
#include "Basics/Common.h"
#include "Basics/debugging.h"
#include "V8/v8-globals.h"
#ifdef _WIN32
#include "Basics/win-utils.h"
#else
#ifdef __APPLE__
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#elif !defined(_MSC_VER)
extern char** environ;
#endif
#endif
static bool canExpose(v8::Isolate* isolate, v8::Local<v8::Name> property) {
if (property->IsSymbol()) {
return false;
}
v8::String::Utf8Value const key(isolate, property);
std::string utf8String(*key, key.length());
if (utf8String == "hasOwnProperty") {
return true;
}
auto& server = arangodb::application_features::ApplicationServer::server();
arangodb::V8SecurityFeature& v8security =
server.getFeature<arangodb::V8SecurityFeature>();
return v8security.shouldExposeEnvironmentVariable(isolate, utf8String);
}
static void EnvGetter(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
if (property->IsSymbol()) {
return args.GetReturnValue().SetUndefined();
}
if (!canExpose(isolate, property)) {
return args.GetReturnValue().SetUndefined();
}
#ifndef _WIN32
v8::String::Utf8Value const key(isolate, property);
char const* val = getenv(*key);
if (val) {
TRI_V8_RETURN_STRING(val);
}
#else // _WIN32
v8::String::Value key(isolate, property);
WCHAR buffer[32767]; // The maximum size allowed for environment variables.
DWORD result = GetEnvironmentVariableW(reinterpret_cast<const WCHAR*>(*key),
buffer, sizeof(buffer));
// ERROR_ENVVAR_NOT_FOUND is possibly returned.
// If result >= sizeof buffer the buffer was too small. That should never
// happen. If result == 0 and result != ERROR_SUCCESS the variable was not
// not found.
if ((result > 0 || GetLastError() == ERROR_SUCCESS) && result < sizeof(buffer)) {
uint16_t const* two_byte_buffer = reinterpret_cast<uint16_t const*>(buffer);
TRI_V8_RETURN(TRI_V8_STRING_UTF16(isolate, two_byte_buffer, result));
}
#endif
// Not found. Fetch from prototype.
TRI_V8_RETURN(args.Data().As<v8::Object>()->Get(property));
}
static void EnvSetter(v8::Local<v8::Name> property, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
if (!canExpose(isolate, property)) {
TRI_V8_RETURN(value);
}
#ifndef _WIN32
v8::String::Utf8Value key(isolate, property);
v8::String::Utf8Value val(isolate, value);
setenv(*key, *val, 1);
#else // _WIN32
v8::String::Value key(isolate, property);
v8::String::Value val(isolate, value);
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
// Environment variables that start with '=' are read-only.
if (key_ptr[0] != L'=') {
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
}
#endif
// Whether it worked or not, always return rval.
TRI_V8_RETURN(value);
}
static void EnvQuery(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Integer>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
int32_t rc = -1; // Not found unless proven otherwise.
if (!canExpose(isolate, property)) {
return;
}
#ifndef _WIN32
v8::String::Utf8Value key(isolate, property);
if (getenv(*key)) {
rc = 0;
}
#else // _WIN32
v8::String::Value key(isolate, property);
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
SetLastError(ERROR_SUCCESS);
if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 || GetLastError() == ERROR_SUCCESS) {
rc = 0;
if (key_ptr[0] == L'=') {
// Environment variables that start with '=' are hidden and read-only.
rc = static_cast<int32_t>(v8::ReadOnly) |
static_cast<int32_t>(v8::DontDelete) | static_cast<int32_t>(v8::DontEnum);
}
}
#endif
if (rc != -1) {
TRI_V8_RETURN(rc);
}
}
static void EnvDeleter(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Boolean>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
if (!canExpose(isolate, property)) {
TRI_V8_RETURN_FALSE();
}
#ifndef _WIN32
v8::String::Utf8Value key(isolate, property);
bool rc = getenv(*key) != nullptr;
if (rc) {
unsetenv(*key);
}
#else
bool rc = true;
v8::String::Value key(isolate, property);
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
if (key_ptr[0] == L'=' || !SetEnvironmentVariableW(key_ptr, nullptr)) {
// Deletion failed. Return true if the key wasn't there in the first place,
// false if it is still there.
rc = GetEnvironmentVariableW(key_ptr, nullptr, 0) == 0 && GetLastError() != ERROR_SUCCESS;
}
#endif
if (rc) {
TRI_V8_RETURN_TRUE();
}
TRI_V8_RETURN_FALSE();
}
static void EnvEnumerator(const v8::PropertyCallbackInfo<v8::Array>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
#ifndef _WIN32
int size = 0;
while (environ[size]) {
size++;
}
v8::Local<v8::Array> envarr = v8::Array::New(isolate);
int j = 0;
for (int i = 0; i < size; ++i) {
char const* var = environ[i];
char const* s = strchr(var, '=');
size_t const length = s ? s - var : strlen(var);
v8::Local<v8::String> name = TRI_V8_PAIR_STRING(isolate, var, length);
if (canExpose(isolate, name)) {
envarr->Set(j++, name);
}
}
#else // _WIN32
WCHAR* environment = GetEnvironmentStringsW();
if (environment == nullptr) return; // This should not happen.
v8::Local<v8::Array> envarr = v8::Array::New(isolate);
WCHAR* p = environment;
int i = 0;
while (*p != 0) {
WCHAR* s;
if (*p == L'=') {
// If the key starts with '=' it is a hidden environment variable.
p += wcslen(p) + 1;
continue;
} else {
s = wcschr(p, L'=');
}
if (!s) {
s = p + wcslen(p);
}
uint16_t const* two_byte_buffer = reinterpret_cast<uint16_t const*>(p);
size_t const two_byte_buffer_len = s - p;
auto value = TRI_V8_STRING_UTF16(isolate, two_byte_buffer, (int)two_byte_buffer_len);
if (canExpose(isolate, value)) {
envarr->Set(i, value);
}
p = s + wcslen(s) + 1;
i++;
}
FreeEnvironmentStringsW(environment);
#endif
TRI_V8_RETURN(envarr);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief stores the V8 utils functions inside the global variable
////////////////////////////////////////////////////////////////////////////////
void TRI_InitV8Env(v8::Isolate* isolate, v8::Handle<v8::Context> context) {
TRI_v8_global_t* v8g = TRI_GetV8Globals(isolate);
v8::Handle<v8::ObjectTemplate> rt;
v8::Handle<v8::FunctionTemplate> ft;
ft = v8::FunctionTemplate::New(isolate);
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ENV"));
rt = ft->InstanceTemplate();
// rt->SetInternalFieldCount(3);
rt->SetHandler(v8::NamedPropertyHandlerConfiguration(EnvGetter, EnvSetter, EnvQuery,
EnvDeleter, EnvEnumerator,
v8::Object::New(isolate)));
v8g->EnvTempl.Reset(isolate, rt);
TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "ENV"),
ft->GetFunction());
}