//////////////////////////////////////////////////////////////////////////////// /// @brief V8 utility functions /// /// @file /// /// DISCLAIMER /// /// Copyright 2014 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 /// /// @author Dr. Frank Celler /// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2011-2014, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 #include "Basics/win-utils.h" #endif #include "v8-utils.h" #include "v8-buffer.h" #include #include "Basics/Dictionary.h" #include "Basics/FileUtils.h" #include "Basics/Nonce.h" #include "Basics/ProgramOptions.h" #include "Basics/RandomGenerator.h" #include "Basics/StringUtils.h" #include "Basics/conversions.h" #include "Basics/csv.h" #include "Basics/files.h" #include "Basics/logging.h" #include "Basics/process-utils.h" #include "Basics/string-buffer.h" #include "Basics/tri-strings.h" #include "Basics/tri-zip.h" #include "Basics/Utf8Helper.h" #include "Rest/HttpRequest.h" #include "Rest/SslInterface.h" #include "SimpleHttpClient/GeneralClientConnection.h" #include "SimpleHttpClient/SimpleHttpClient.h" #include "SimpleHttpClient/SimpleHttpResult.h" #include "Statistics/statistics.h" #include "V8/v8-conv.h" #include "V8/v8-globals.h" #include "unicode/normalizer2.h" #include "3rdParty/valgrind/valgrind.h" using namespace std; using namespace triagens::arango; using namespace triagens::basics; using namespace triagens::httpclient; using namespace triagens::rest; // ----------------------------------------------------------------------------- // --SECTION-- GENERAL // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief Random string generators //////////////////////////////////////////////////////////////////////////////// namespace { static Random::UniformCharacter JSAlphaNumGenerator("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); static Random::UniformCharacter JSNumGenerator("0123456789"); static Random::UniformCharacter JSSaltGenerator("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*(){}[]:;<>,.?/|"); } // ----------------------------------------------------------------------------- // --SECTION-- public classes // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief Converts an object to a UTF-8-encoded and normalized character array. //////////////////////////////////////////////////////////////////////////////// TRI_Utf8ValueNFC::TRI_Utf8ValueNFC (TRI_memory_zone_t* memoryZone, v8::Handle const obj) : _str(nullptr), _length(0), _memoryZone(memoryZone) { v8::String::Value str(obj); int str_len = str.length(); _str = TRI_normalize_utf16_to_NFC(_memoryZone, *str, (size_t) str_len, &_length); } //////////////////////////////////////////////////////////////////////////////// /// @brief Destroys a normalized string object //////////////////////////////////////////////////////////////////////////////// TRI_Utf8ValueNFC::~TRI_Utf8ValueNFC () { if (_str != nullptr) { TRI_Free(_memoryZone, _str); } } // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief create a Javascript error object //////////////////////////////////////////////////////////////////////////////// static void CreateErrorObject (v8::Isolate* isolate, int errorNumber, string const& message) { if (errorNumber == TRI_ERROR_OUT_OF_MEMORY) { LOG_ERROR("encountered out-of-memory error"); } v8::Handle errorMessage = TRI_V8_STD_STRING(message); if (errorMessage.IsEmpty()) { isolate->ThrowException(v8::Object::New(isolate)); return; } v8::Handle err = v8::Exception::Error(errorMessage); if (err.IsEmpty()) { isolate->ThrowException(v8::Object::New(isolate)); return ; } v8::Handle errorObject = err->ToObject(); if (errorObject.IsEmpty()) { isolate->ThrowException(v8::Object::New(isolate)); return ; } errorObject->Set(TRI_V8_ASCII_STRING("errorNum"), v8::Number::New(isolate, errorNumber)); errorObject->Set(TRI_V8_ASCII_STRING("errorMessage"), errorMessage); TRI_GET_GLOBALS(); TRI_GET_GLOBAL(ArangoErrorTempl, v8::ObjectTemplate); v8::Handle ArangoError = ArangoErrorTempl->NewInstance(); if (! ArangoError.IsEmpty()) { errorObject->SetPrototype(ArangoError); } isolate->ThrowException(errorObject); } //////////////////////////////////////////////////////////////////////////////// /// @brief reads/execute a file into/in the current context //////////////////////////////////////////////////////////////////////////////// static bool LoadJavaScriptFile (v8::Isolate* isolate, char const* filename, bool execute, bool useGlobalContext) { v8::HandleScope handleScope(isolate); size_t length; char* content = TRI_SlurpFile(TRI_UNKNOWN_MEM_ZONE, filename, &length); if (content == nullptr) { LOG_TRACE("cannot load java script file '%s': %s", filename, TRI_last_error()); return false; } if (useGlobalContext) { char const* prologue = "(function() { "; char const* epilogue = "/* end-of-file */ })()"; char* contentWrapper = TRI_Concatenate3StringZ(TRI_UNKNOWN_MEM_ZONE, prologue, content, epilogue); TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, content); length += strlen(prologue) + strlen(epilogue); content = contentWrapper; } if (content == nullptr) { LOG_TRACE("cannot load java script file '%s': %s", filename, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY)); return false; } v8::Handle name = TRI_V8_STRING(filename); v8::Handle source = TRI_V8_PAIR_STRING(content, (int) length); TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, content); v8::Handle script = v8::Script::Compile(source, name); // compilation failed, print errors that happened during compilation if (script.IsEmpty()) { return false; } if (execute) { // execute script v8::Handle result = script->Run(); if (result.IsEmpty()) { return false; } } LOG_TRACE("loaded java script file: '%s'", filename); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief reads all files from a directory into the current context //////////////////////////////////////////////////////////////////////////////// static bool LoadJavaScriptDirectory (v8::Isolate* isolate, char const* path, bool execute, bool useGlobalContext) { v8::HandleScope scope(isolate); TRI_vector_string_t files; bool result; regex_t re; size_t i; LOG_TRACE("loading JavaScript directory: '%s'", path); files = TRI_FilesDirectory(path); regcomp(&re, "^(.*)\\.js$", REG_ICASE | REG_EXTENDED); result = true; for (i = 0; i < files._length; ++i) { v8::TryCatch tryCatch; bool ok; char const* filename; char* full; filename = files._buffer[i]; if (regexec(&re, filename, 0, 0, 0) != 0) { continue; } full = TRI_Concatenate2File(path, filename); ok = LoadJavaScriptFile(isolate, full, execute, useGlobalContext); TRI_FreeString(TRI_CORE_MEM_ZONE, full); result = result && ok; if (! ok) { if (tryCatch.CanContinue()) { TRI_LogV8Exception(isolate, &tryCatch); } else { TRI_GET_GLOBALS(); v8g->_canceled = true; } } } TRI_DestroyVectorString(&files); regfree(&re); return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a distribution vector //////////////////////////////////////////////////////////////////////////////// static v8::Handle DistributionList (v8::Isolate* isolate, StatisticsVector const& dist) { v8::EscapableHandleScope scope(isolate); v8::Handle result = v8::Array::New(isolate); for (uint32_t i = 0; i < (uint32_t) dist._value.size(); ++i) { result->Set(i, v8::Number::New(isolate, dist._value[i])); } return scope.Escape(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief fills the distribution //////////////////////////////////////////////////////////////////////////////// static void FillDistribution (v8::Isolate* isolate, v8::Handle list, v8::Handle name, StatisticsDistribution const& dist) { v8::Handle result = v8::Object::New(isolate); result->Set(TRI_V8_ASCII_STRING("sum"), v8::Number::New(isolate, dist._total)); result->Set(TRI_V8_ASCII_STRING("count"), v8::Number::New(isolate, (double) dist._count)); v8::Handle counts = v8::Array::New(isolate, (int) dist._counts.size()); uint32_t pos = 0; for (vector::const_iterator i = dist._counts.begin(); i != dist._counts.end(); ++i, ++pos) { counts->Set(pos, v8::Number::New(isolate, (double) *i)); } result->Set(TRI_V8_ASCII_STRING("counts"), counts); list->Set(name, result); } // ----------------------------------------------------------------------------- // --SECTION-- JS functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief returns the program options //////////////////////////////////////////////////////////////////////////////// static void JS_Options (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); if (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("options()"); } auto json = triagens::basics::ProgramOptions::getJson(); if (json != nullptr) { auto result = TRI_ObjectJson(isolate, json); // remove this variable result->ToObject()->ForceDelete(TRI_V8_STRING("server.password")); TRI_V8_RETURN(result); } TRI_V8_RETURN(v8::Object::New(isolate)); } //////////////////////////////////////////////////////////////////////////////// /// @brief decodes a base64-encoded string /// /// @FUN{internal.base64Decode(@FA{value})} /// /// Base64-decodes the string @FA{value}. //////////////////////////////////////////////////////////////////////////////// static void JS_Base64Decode(const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("base64Decode()"); } try { string const value = TRI_ObjectToString(args[0]); string const base64 = StringUtils::decodeBase64(value); TRI_V8_RETURN_STD_STRING(base64); } catch (...) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_errno(), TRI_last_error()); } TRI_ASSERT(false); } //////////////////////////////////////////////////////////////////////////////// /// @brief encodes a string as base64 /// /// @FUN{internal.base64Encode(@FA{value})} /// /// Base64-encodes the string @FA{value}. //////////////////////////////////////////////////////////////////////////////// static void JS_Base64Encode (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("base64Encode()"); } try { string const&& value = TRI_ObjectToString(args[0]); string const&& base64 = StringUtils::encodeBase64(value); TRI_V8_RETURN_STD_STRING(base64); } catch (...) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_errno(), TRI_last_error()); } TRI_ASSERT(false); } //////////////////////////////////////////////////////////////////////////////// /// @brief parses a Javascript snippet, but does not execute it /// /// @FUN{internal.parse(@FA{script})} /// /// Parses the @FA{script} code, but does not execute it. /// Will return *true* if the code does not have a parse error, and throw /// an exception otherwise. //////////////////////////////////////////////////////////////////////////////// static void JS_Parse (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); if (args.Length() < 1) { TRI_V8_THROW_EXCEPTION_USAGE("parse(