diff --git a/src/desktop/adapters/sqlite3.js b/src/desktop/adapters/sqlite3.js new file mode 100644 index 0000000..fead2b9 --- /dev/null +++ b/src/desktop/adapters/sqlite3.js @@ -0,0 +1,678 @@ +/** +* @file A SQLite 3 adapter for data storage and retrieval in CypherPoker.JS +* +* @version 0.0.1 +* @author Patrick Bay +* @copyright MIT License +*/ + +const {spawn} = require('child_process'); +const filesystem = require('fs'); + +/** +* @property {Object} sqlite3=null The child SQLite 3 process managed by this script. +*/ +var sqlite3 = null; +/** +* @property {Object} initData=null The dynamic initialization data object received and +* processed by the {@link initialize} function. +*/ +var initData = null; +/** +* @property {Boolean} dbOpened=true True if a valid SQLite 3 database file was opened +* via the {@link openDBFile} function. Database functionality may still be available +* if this value is false but the database will probably be in-memory only unless +* manually saved or exported. +*/ +var dbOpened = false; +/** +* @property {Boolean} firstCapture=true True if the adapter is awaiting the first output +* from the child process' STDOUT output; set to false afterward. +*/ +var firstCapture = true; +/** +* @property {Boolean} busy=true True if the adapter is currently busy processing a +* request, false if it's available. +*/ +var busy = true; +/** +* @property {Array} requestQueue Array of requests queued if {@link busy} is true. +* The newest request is at the end of the array and the oldest at the beginning. +*/ +var requestQueue = new Array();; +/** +* @property {Promise} currentPromise=null The promise representing the binary operation currently +* being processed. +*/ +var currentPromise = null; +/** +* @property {Number} dbMaxMB=500 The maximum allowable size of the SQLite 3 database file. +*/ +const dbMaxMB = 500; +/** +* @property {Boolean} logErrors=true Sends the SQLite 3 binary's error output to the error console if +* true. +*/ +const logErrors = true; + +/** +* Initializes the adapter and launches the binary appropriate to the detected platform +* and system architecture. This function is usually invoked by the parent host. +* +* @param {Object} initObj The initialization data object for the adapter. +*/ +function initialize(initObj) { + console.log ("Initializing SQLite 3 adapter..."); + var promise = new Promise(function(resolve, reject) { + initData = initObj; + var os = ""; + var bin = ""; + switch (process.platform) { + case "win32": + os = "win"; + //currently only 32-bit binaries are available for both architectures + if (process.arch == "x32") { + os += "32"; + } else if (process.arch == "x64") { + os += "32"; + } else { + throw (new Error ("Unrecognized hardware architecture: "+process.arch)); + } + bin = "sqlite3.exe"; + break; + case "linux": + os = "linux"; + if (process.arch == "x32") { + os += "32"; + } else if (process.arch == "x64") { + os += "64"; + } else { + throw (new Error ("Unrecognized hardware architecture: "+process.arch)); + } + bin = "sqlite3"; + break; + case "darwin": + os = "osx"; + //todo: find out how OSX hardware architectures differ + if (process.arch == "x32") { + os += ""; + } else if (process.arch == "x64") { + os += ""; + } else { + throw (new Error ("Unrecognized OS: "+process.platform)); + } + bin = "sqlite3"; + break; + default: + throw (new Error ("Unrecognized hardware architecture: "+process.arch)); + break; + } + initData.bin = initData.bin.split("%os%").join(os).split("%bin%").join(bin); + initData.bin = filesystem.realpathSync.native(initData.bin); //convert to native path + console.log ("Executing SQLite 3 binary: "+initData.bin); + var options = new Object(); + options.cwd = "."; //process working directory + options.shell = true; //hide console window + options.windowsHide = true; //hide console window + //MUST include "-interactive" flag in order to receive STDOUT output: + sqlite3 = spawn(initData.bin, ["-interactive"], options); + //add output and process close handlers + sqlite3.stdout.on('data', onSTDOUT); + sqlite3.stderr.on('data', onSTDERR); + sqlite3.on('close', onProcessClose); + initData.dbFilePath = null; //no file open yet (using in-memory db by default) + currentPromise = new Object(); + currentPromise.resolve = resolve; + currentPromise.reject = reject; + console.log ("SQLite3 adapter initialized."); + }); + return (promise); +} + +/** +* Handles the console (STDOUT) output of the SQLite 3 binary. +* +* @param {Buffer} data The raw data output of the binary. +*/ +function onSTDOUT(data) { + if (firstCapture == false) { + //process command or query result + if (currentPromise != null) { + var results = collateResults(data.toString(), currentPromise.schema); + currentPromise.resolve(results); + } + busy = false; + processRequestQueue(); + } else { + //process startup messages + var versionStr = data.toString().split("\n")[0]; + console.log (versionStr); + firstCapture = false; + currentPromise.resolve(true); + busy = false; + processRequestQueue(); + } +} + +/** +* Handles the error (STDERR) output of the SQLite 3 binary. +* +* @param {Buffer} data The error output of the binary. +*/ +function onSTDERR(data) { + if (logErrors) { + console.error (data.toString()); + } + if (firstCapture == false) { + currentPromise.reject(data.toString()); + currentPromise = null; + } +} + +/** +* Event handler invoked when the child SQLite 3 binary process terminates. +* +* @param {Number} code The exit code with which the process ended. +*/ +function onProcessClose(code) { + //we don't expect the process to terminate while the application is running so we + // display an error message: + console.error ("SQLite3 process has terminated with code "+code); + busy = true; //prevent any additional attempts to communicate with the process + firstCapture = true; //in case it's restarted +} + + +/** +* Issues a file open command to the running SQLite 3 binary. Note that the binary +* automatically updates the opened file so no "save" function is provided. +* +* @param {String} dbFilePath The filesystem path to the SQLite 3 database file to +* open. If the file doesn't exist it will be created if possible. +*/ +function openDBFile(dbFilePath) { + initData.dbFilePath = dbFilePath; + var promise = new Promise(function(resolve, reject) { + sqlite3.stdin.write(".open "+initData.dbFilePath+"\n"); //issue ".open" command + currentPromise = new Object(); + //use a non-standard structure for this call: + currentPromise.resolve = onOpenDBFile; + currentPromise.openResolve = resolve; + currentPromise.reject = onOpenDBFileFail; + currentPromise.openReject = reject; + }); + return (promise); +} + +/** +* Function invoked when a database file is successfully opened via the {@link openDBFile} +* function. +*/ +function onOpenDBFile() { + dbOpened = true; + currentPromise.openResolve(true); +} + +/** +* Function invoked when a database cannot be opened or created via the {@link openDBFile} +* function. +*/ +function onOpenDBFileFail() { + dbOpened = false; + currentPromise.openReject(false); +} + +/** +* Returns the size of a filesystem file. +* +* @param {String} filePath The filesystem path of the file to analyze. +* @param {String} [units="MB"] The case-sensitive units in which to return the resulting +* file size in. Valid values include: KB (kilobytes), MB (megabytes), +* and GB (gigabytes). If omitted or not one of the valid values then the result is +* returned in bytes. +* +* @return {Number} The size of the file specified by filePath in the specified +* units. +*/ +function getFileSize(filePath, units="MB") { + var stats = filesystem.statSync(filePath); + var fileSize = stats.size; + if (units == "KB") { + fileSize /= 1000; + } else if (units == "MB") { + fileSize /= 1000000; + } else if (units == "GB") { + fileSize /= 1000000000; + } else { + //assume bytes + } + return (fileSize); +} + +/** +* Parses a table schema result and returns it as an associate array for use +* with subsequent queries. +* +* @param {Array} resultArray The indexed result of a table schema (usually "PRAGMA") query. +* +* @return {Array} The parsed database schema as an indexed array with each element +* containing a column name and type. +*/ +function parseSchema(resultArray) { + var schemaArray = new Array(); + for (var count=0; count < resultArray.length; count++) { + var currentLine = resultArray[count]; + var columnData = new Object(); + columnData.name = currentLine[1]; + columnData.type = currentLine[2]; + schemaArray.push(columnData); + } + return (schemaArray); +} + +/** +* Parses and collates a query result using an optional schema definition. +* +* @param {String} resultData The raw query result string (for example, as that captured +* by onSTDOUT). +* @param {Array} [schemaArray=null] The optional schema array, as generated by a {@link parseSchema} +* call. If omitted, the data returned is anonymous (no column names are included). +* @param {String} columnDelimiter="|" The delimiter used to separate column data in the resultData. +* @param {String} rowDelimiter="\r\n" The delimiter used to separate rows in the resultData. +* +* @return {Array} An indexed array of collated results. If the schemaArray is supplied then each +* array element contains a name-value combination (where the name is the column name), otherwise only the value +* is containing in each element. +*/ +function collateResults(resultData, schemaArray=null, columnDelimiter="|", rowDelimiter="\r\n") { + var resultLines = resultData.split(rowDelimiter); + var resultArray = new Array(); + //final line is prompt so omit it + for (var count=0; count < (resultLines.length-1); count++) { + var currentLine = resultLines[count]; + var rowArray = currentLine.split(columnDelimiter); + if (schemaArray == null) { + //value only (no column names) + resultArray.push(rowArray); + } else { + var resultObj = new Object(); + for (var count2=0; count2 0) { + jsonObj.result = result; + } else { + jsonObj = buildJSONRPC("2.0", false); + jsonObj.error.code = -32602; + jsonObj.error.message = "No matching account."; + } + } catch (err) { + var jsonObj = buildJSONRPC("2.0", false); + jsonObj.error.code = -32603; + jsonObj.error.message = err; + } + return (jsonObj); +} + +/** +* Stores an account record to the current CypherPoker.JS server, usually in response to +* a "putrecord" API call. +* +* @param {Object} requestObj The JSON-RPC 2.0 request containing the parameters of the +* account to store. +* +* @return {Object} A JSON-RPC 2.0 result object containing an "OK" result if successful +* or a JSON-RPC 2.0 error object containing a description of the error generated during the +* attempt. +* @async +*/ +async function putAccountRecord(requestObj) { + var sizeMB = getFileSize(initData.dbFilePath, "MB"); + if (sizeMB >= dbMaxMB) { + var jsonObj = buildJSONRPC("2.0", false); + jsonObj.error.code = -32603; + jsonObj.error.message = "Database limit exceeded."; + return (jsonObj); + } + try { + var message = requestObj.params.message; + var querySQL = "INSERT INTO `accounts` (`type`, `network`, `chain`, `addressIndex`, `address`, `pwhash`, `balance`,`updated`) VALUES ("; + querySQL += "\""+message.type+"\","; + querySQL += "\""+message.network+"\","; + querySQL += message.chain+","; + querySQL += message.addressIndex+","; + querySQL += "\""+message.address+"\","; + querySQL += "\""+message.pwhash+"\","; + querySQL += "\""+message.balance+"\","; + querySQL += "\""+message.updated+"\""; + querySQL += ");"; + var jsonObj = buildJSONRPC(); + var result = await query(querySQL); + jsonObj.result = "OK"; + } catch (err) { + jsonObj = buildJSONRPC("2.0", false); + jsonObj.error.code = -32603; + jsonObj.error.message = err; + } + return (jsonObj); +} + +/** +* Updates an account record to the current CypherPoker.JS server, usually in response to +* a "updaterecord" API call. Currently only the "updated" property of the account is +* changed with this call. For updating any other information use the {@link putAccountRecord} +* function in order to maintain a history of changes. +* +* @param {Object} requestObj The JSON-RPC 2.0 request containing the parameters of the +* account to update. +* +* @return {Object} A JSON-RPC 2.0 result object containing an "OK" result if successful +* or a JSON-RPC 2.0 error object containing a description of the error generated during the +* attempt. +* @async +*/ +async function updateAccountRecord(requestObj) { + var sizeMB = getFileSize(initData.dbFilePath, "MB"); + if (sizeMB >= dbMaxMB) { + var jsonObj = buildJSONRPC("2.0", false); + jsonObj.error.code = -32603; + jsonObj.error.message = "Database limit exceeded."; + return (jsonObj); + } + try { + var jsonObj = buildJSONRPC(); + var message = requestObj.params.message; + var querySQL = "UPDATE `accounts` SET `updated`=\""+message.updated+"\" WHERE `primary_key`="+message.primary_key+";"; + var result = await query(querySQL); + jsonObj.result = "OK"; + } catch (err) { + jsonObj = buildJSONRPC("2.0", false); + jsonObj.error.code = -32603; + jsonObj.error.message = err; + } + return (jsonObj); +} + +/** +* Returns the number of seconds elapsed since the last update of any account in the database. +* +* @return {Number} The number of seconds elapsed since the last update of any account in the database. +*/ +function getElapsedUpdateSeconds(latestUpdate) { + var now = new Date(); + var elapsedSeconds = (date.valueOf() - latestUpdate.valueOf()) / 1000; + if (elapsedSeconds < 0) { + elapsedSeconds = 0; + } + return (elapsedSeconds); +} + +/** +* Invokes an asynchronous request to the adapter, usually by a hosted application module +* (e.g. server API function). If the adapter is currently {@link busy} then the +* request is queued using {@link queueRequest}, otherwise it is processed immediately via +* {@link processRequest}. +* +* @param {Object} requestObj An object containing details of the request. +* @param {String} requestObj.method A database access method to invoke. +* @param {Object} requestObj.message Additional data included with the method +* such as a search parameter. +* @param {Function} requestObj.resolve The resolve method supplied in the caller's +* generated Promise to call on a successful result. +* @param {Function} requestObj.reject The reject method supplied in the caller's +* generated Promise to call on a failure. +* +* @return {Promise} The promise resolves with the result of the invocation or rejects +* with an error. +*/ +function invoke(requestObj) { + var queueObj = new Object(); + queueObj.requestObj = requestObj; + var promise = new Promise(function(resolve, reject) { + queueObj.resolve = resolve; + queueObj.reject = reject; + }); + if (busy == false) { + processRequest(queueObj); + } else { + queueRequest(queueObj); + } + return (promise); +} + +/** +* Queues a request made through the {@link invoke} function, usually if the adapter +* is currently {@link busy}, to the {@link requestQueue}. +* +* @param {Object} queueObj An object containing the request and promise references to +* queue. +* @param {Object} queueObj.requestObj Contains the details and parameters of the +* request being queued. +* @param {Function} queueObj.resolve The associated Promise's resolve function +* to be invoked when the request has successfully been processed. +* @param {Function} queueObj.reject The associated Promise's reject function +* to be invoked when the request has been rejected (an error thrown or an invalid result received). +*/ +function queueRequest(queueObj) { + requestQueue.push(queueObj); +} + +/** +* Removes the first queued request in the {@link requestQueue} and +* processes it via {@link processRequest}. +*/ +function processRequestQueue() { + if (requestQueue.length > 0) { + var queueObj = requestQueue.shift(); + processRequest(queueObj); + } +} + +/** +* Immediately processes a queued request made to the adapter. +* +* @param {Object} queueObj An object containing the queued request and promise references. +* @param {Object} queueObj.requestObj Contains the details and parameters of the +* queued request. +* @param {Function} queueObj.resolve The associated Promise's resolve function +* to be invoked when the request has successfully been processed. +* @param {Function} queueObj.reject The associated Promise's reject function +* to be invoked when the request has been rejected (an error thrown or an invalid result received). +*/ +function processRequest(queueObj) { + busy = true; + var requestObj = queueObj.requestObj; + var resolve = queueObj.resolve; + var reject = queueObj.reject; + switch (requestObj.method) { + case "walletstatus": + getWalletStatus().then(resultObj => { + resolve(resultObj); + busy = false; + processRequestQueue(); + }).catch(errorObj => { + reject(errorObj); + busy = false; + processRequestQueue(); + }); + break; + case "getrecord": + getAccountRecord(requestObj).then(resultObj => { + resolve(resultObj); + busy = false; + processRequestQueue(); + }).catch(errorObj => { + reject(errorObj); + busy = false; + processRequestQueue(); + }); + break; + case "putrecord": + putAccountRecord(requestObj).then(resultObj => { + resolve(resultObj); + busy = false; + processRequestQueue(); + }).catch(errorObj => { + reject(errorObj); + busy = false; + processRequestQueue(); + }); + break; + case "updaterecord": + updateAccountRecord(requestObj).then(resultObj => { + resolve(resultObj); + busy = false; + processRequestQueue(); + }).catch(errorObj => { + reject(errorObj); + busy = false; + processRequestQueue(); + }); + break; + default: + var errorObj = buildJSONRPC("2.0", false); + errorObj.error.code = -32601; + errorObj.error.message = "Method "+requestObj.method+" not found."; + reject (errorObj); + busy = false; + processRequestQueue(); + break; + } +} + +/** +* Executes an asynchronous query on the database. +* +* @param {String} SQL The query to execute. +* @param {Array} schemaArray=null A parsed table schema array to apply to to the query result. +* If omitted, each result row's column is returned as an indexed element rather than +* a named one. +* +* @return {Promise} The promise resolves with an array of results with each element containing +* a result row. Each result row will either contain indexed (anonymous) column values if +* schema is null or name-value pairs if a matching table schema is +* supplied. A standard Error object is included with a rejection. +*/ +function query(SQL, schemaArray=null) { + var promise = new Promise(function(resolve, reject) { + currentPromise = new Object(); + currentPromise.schema = schemaArray; + currentPromise.resolve = resolve; + currentPromise.reject = reject; + sqlite3.stdin.write(SQL+"\n"); + }); + return (promise); +} + +/** +* Builds a JSON-RPC message object. +* +* @param {String} [version="2.0"] The JSON-RPC version to designate the object as. +* Currently only JSON-RPC 2.0 message formatting is supported and other versions +* will throw an error. If this parameter is null, the default value is assumed. +* @param {Boolean} [isResult=true] True if this is a result object or +* notification, false if it's an error. +* @param {Boolean} [includeUniqueID=false] A uniquely generated message ID +* will be generated if true otherwise no ID is included (e.g. notification). +*/ +function buildJSONRPC(version="2.0", isResult=true, includeUniqueID=false) { + var jsonObj = new Object(); + if (version == null) { + version = "2.0"; + } + version = version.trim(); + if (version != "2.0") { + throw (new Error("Unsupported JSON-RPC message format version (\"" + version + "\")")); + } + jsonObj.jsonrpc = version; + if (includeUniqueID) { + jsonObj.id = String(Date.now()).split("0.").join(""); + } + if (isResult) { + jsonObj.result = new Object(); + } else { + jsonObj.error = new Object(); + jsonObj.error.message = "An error occurred."; + jsonObj.error.code = -32603; //internal error + } + return (jsonObj); +} diff --git a/src/desktop/bin/sqlite/LICENSE.md b/src/desktop/bin/sqlite/LICENSE.md new file mode 100644 index 0000000..a4eb898 --- /dev/null +++ b/src/desktop/bin/sqlite/LICENSE.md @@ -0,0 +1,29 @@ +SQLite is in the Public Domain + +All of the code and documentation in SQLite has been dedicated to the public domain by the authors. All code authors, and representatives of the companies they work for, have signed affidavits dedicating their contributions to the public domain and originals of those signed affidavits are stored in a firesafe at the main offices of Hwaci. Anyone is free to copy, modify, publish, use, compile, sell, or distribute the original SQLite code, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. + +The previous paragraph applies to the deliverable code and documentation in SQLite - those parts of the SQLite library that you actually bundle and ship with a larger application. Some scripts used as part of the build process (for example the "configure" scripts generated by autoconf) might fall under other open-source licenses. Nothing from these build scripts ever reaches the final deliverable SQLite library, however, and so the licenses associated with those scripts should not be a factor in assessing your rights to copy and use the SQLite library. + +All of the deliverable code in SQLite has been written from scratch. No code has been taken from other projects or from the open internet. Every line of code can be traced back to its original author, and all of those authors have public domain dedications on file. So the SQLite code base is clean and is uncontaminated with licensed code from other projects. + +Open-Source, not Open-Contribution + +SQLite is open-source, meaning that you can make as many copies of it as you want and do whatever you want with those copies, without limitation. But SQLite is not open-contribution. In order to keep SQLite in the public domain and ensure that the code does not become contaminated with proprietary or licensed content, the project does not accept patches from unknown persons. + +All of the code in SQLite is original, having been written specifically for use by SQLite. No code has been copied from unknown sources on the internet. + +Warranty of Title + +SQLite is in the public domain and does not require a license. Even so, some organizations want legal proof of their right to use SQLite. Circumstances where this occurs include the following: + +- Your company desires indemnity against claims of copyright infringement. +- You are using SQLite in a jurisdiction that does not recognize the public domain. +- You are using SQLite in a jurisdiction that does not recognize the right of an author to dedicate their work to the public domain. +- You want to hold a tangible legal document as evidence that you have the legal right to use and distribute SQLite. +- Your legal department tells you that you have to purchase a license. + +If any of the above circumstances apply to you, Hwaci, the company that employs all the developers of SQLite, will sell you a Warranty of Title for SQLite. A Warranty of Title is a legal document that asserts that the claimed authors of SQLite are the true authors, and that the authors have the legal right to dedicate the SQLite to the public domain, and that Hwaci will vigorously defend against challenges to those claims. All proceeds from the sale of SQLite Warranties of Title are used to fund continuing improvement and support of SQLite. + +Contributed Code + +In order to keep SQLite completely free and unencumbered by copyright, the project does not accept patches. If you would like to make a suggested change, and include a patch as a proof-of-concept, that would be great. However please do not be offended if we rewrite your patch from scratch. diff --git a/src/desktop/bin/sqlite/README.md b/src/desktop/bin/sqlite/README.md new file mode 100644 index 0000000..e4b61fa --- /dev/null +++ b/src/desktop/bin/sqlite/README.md @@ -0,0 +1,9 @@ +## SQLite Executable binaries + +The sub-directories in this directory contain the native SQLite executable binaries for all supported operating systems. + +All binaries were downloaded from the official SQLite site (https://sqlite.org/download.html), except for the 64-bit Linux executables found in the linux64 folder which were downloaded from Bora M. Alper's build repository (https://github.com/boramalper/sqlite3-x64/releases). + +Required files for building the 64-bit Linux executables are included in the linux64/build directory which is a copy of the repository trunk (https://github.com/boramalper/sqlite3-x64). Refer to the linux64/build/README.md file for instructions on building these binaries from source code. + +Currently only Windows 32-bit binaries (win32) are available for both 32 and 64-bit architectures. diff --git a/src/desktop/bin/sqlite/linux32/sqldiff b/src/desktop/bin/sqlite/linux32/sqldiff new file mode 100644 index 0000000..8c4eaff Binary files /dev/null and b/src/desktop/bin/sqlite/linux32/sqldiff differ diff --git a/src/desktop/bin/sqlite/linux32/sqlite3 b/src/desktop/bin/sqlite/linux32/sqlite3 new file mode 100644 index 0000000..1a57e06 Binary files /dev/null and b/src/desktop/bin/sqlite/linux32/sqlite3 differ diff --git a/src/desktop/bin/sqlite/linux32/sqlite3_analyzer b/src/desktop/bin/sqlite/linux32/sqlite3_analyzer new file mode 100644 index 0000000..6b4f09e Binary files /dev/null and b/src/desktop/bin/sqlite/linux32/sqlite3_analyzer differ diff --git a/src/desktop/bin/sqlite/linux64/CHECKSUMS.sha1 b/src/desktop/bin/sqlite/linux64/CHECKSUMS.sha1 new file mode 100644 index 0000000..51462c6 --- /dev/null +++ b/src/desktop/bin/sqlite/linux64/CHECKSUMS.sha1 @@ -0,0 +1,3 @@ +7d9292d97ee6a2519cc803bab1cea759d9c136b8 sqldiff +90b1a0fc9b689f20010269b1473f29da83ff0833 sqlite3 +43d3a9180b410cfeb4ed7c407280a41a5c17c3f0 sqlite3_analyzer diff --git a/src/desktop/bin/sqlite/linux64/NOTICE b/src/desktop/bin/sqlite/linux64/NOTICE new file mode 100644 index 0000000..0206e17 --- /dev/null +++ b/src/desktop/bin/sqlite/linux64/NOTICE @@ -0,0 +1 @@ +These are UNOFFICIAL SQLite precompiled binaries for linux x64, see https://github.com/boramalper/sqlite3-x64 for details. diff --git a/src/desktop/bin/sqlite/linux64/build/.travis.yml b/src/desktop/bin/sqlite/linux64/build/.travis.yml new file mode 100644 index 0000000..71b7805 --- /dev/null +++ b/src/desktop/bin/sqlite/linux64/build/.travis.yml @@ -0,0 +1,135 @@ +# sudo: false is faster as it is containerised instead of virtualised. +# https://docs.travis-ci.com/user/reference/overview/#virtualisation-environment-vs-operating-system +sudo: false +dist: xenial + +language: c +compiler: gcc + +# $ ./configure --help +# +# Some influential environment variables: +# CC C compiler command +# CFLAGS C compiler flags +# LDFLAGS linker flags, e.g. -L if you have libraries in a +# nonstandard directory +# LIBS libraries to pass to the linker, e.g. -l +# CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if +# you have headers in a nonstandard directory +# CPP C preprocessor +# TCLLIBDIR Where to install tcl plugin +# +# Use these variables to override the choices made by `configure' or to help +# it to find libraries and programs with nonstandard names/locations. +# +# Also: https://docs.travis-ci.com/user/environment-variables/#defining-public-variables-in-travisyml +env: + global: + # https://www.sqlite.org/cli.html#_do_it_yourself_builds_ + # + # The following additional compile-time options are recommended in order to + # provide a full-featured command-line shell: + # + # * -DSQLITE_THREADSAFE=0 + # * -DSQLITE_ENABLE_EXPLAIN_COMMENTS + # * -DSQLITE_USE_ZLIB + # * -DSQLITE_INTROSPECTION_PRAGMAS + # * -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + # * -DSQLITE_ENABLE_STMTVTAB + # * -DSQLITE_ENABLE_DBPAGE_VTAB + # * -DSQLITE_ENABLE_DBSTAT_VTAB + # * -DSQLITE_ENABLE_OFFSET_SQL_FUNC + # * -DSQLITE_ENABLE_JSON1 + # * -DSQLITE_ENABLE_RTREE + # * -DSQLITE_ENABLE_FTS4 + # * -DSQLITE_ENABLE_FTS5 + # + # + # https://www.sqlite.org/howtocompile.html#compiling_the_command_line_interface + # + # [...] to get command-line editing support: + # + # * -DHAVE_READLINE + # * -lreadline + # * -lncurses + # + # !! We statically link readline (and history) due to readline 6 not being + # !! present on later Ubuntu versions. + # !! See https://utcc.utoronto.ca/~cks/space/blog/linux/Ubuntu1804ReadlineMess + - CFLAGS="-O2 -march=x86-64 -I./ -I/usr/include/tcl -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_USE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_OFFSET_SQL_FUNC -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DHAVE_READLINE" + - LDFLAGS="-march=x86-64" + - LIBS="-ldl -lpthread -lz -lm -ltcl -l:libreadline.a -l:libhistory.a -lncurses -ltermcap" + +branches: + only: + - master + +install: + - "wget -i sqlite-src.url" + - "sha1sum --check sqlite-src.sha1" + - "unzip sqlite-src-*.zip" + - rm sqlite-src-*.zip + - mv sqlite-src-* sqlite-src + +before_script: + - "set -o allexport; source ./env; set +o allexport" + - "cd sqlite-src" + + - "./configure" + - "make sqlite3.c" + - "cc -o sqlite3.o -c sqlite3.c ${CFLAGS} ${LDFLAGS} ${LIBS}" + + # sqldiff + - "cc -o sqldiff.o -c tool/sqldiff.c ${CFLAGS} ${LDFLAGS} ${LIBS}" + - "cc -o sqldiff sqlite3.o sqldiff.o ${CFLAGS} ${LDFLAGS} ${LIBS}" + - "sha1sum sqldiff | tee CHECKSUMS.sha1" + + # sqlite3 + - "make shell.c" + - "cc -o shell.o -c shell.c ${CFLAGS} ${LDFLAGS} ${LIBS}" + - "cc -o sqlite3 sqlite3.o shell.o ${CFLAGS} ${LDFLAGS} ${LIBS}" + - "sha1sum sqlite3 | tee -a CHECKSUMS.sha1" + + # sqlite3_analyzer + - "make sqlite3_analyzer.c" + - "cc -o sqlite3_analyzer -c sqlite3_analyzer.c ${CFLAGS} ${LDFLAGS} ${LIBS}" + - "sha1sum sqlite3_analyzer | tee -a CHECKSUMS.sha1" + + # zip the binaries + - "mkdir sqlite-tools-linux-x64-${S64_VERSION}" + - "echo These are UNOFFICIAL SQLite precompiled binaries for linux x64, see https://github.com/boramalper/sqlite3-x64 for details. > sqlite-tools-linux-x64-${S64_VERSION}/NOTICE" + - "cp sqldiff sqlite3 sqlite3_analyzer CHECKSUMS.sha1 sqlite-tools-linux-x64-${S64_VERSION}/" + - "zip -r sqlite-tools-linux-x64-${S64_VERSION}.zip sqlite-tools-linux-x64-${S64_VERSION}/ -9 -v" + - "sha1sum sqlite-tools-linux-x64-${S64_VERSION}.zip | tee sqlite-tools-linux-x64-${S64_VERSION}.zip.sha1" + +script: + - "echo Skipping 'script' stage in Travis CI..." + +before_deploy: + # Set up git user name and tag this commit + - git config --local user.name "Bora M. Alper" + - git config --local user.email "bora@boramalper.org" + # $ git help check-ref-format + # + # 2. They must contain at least one /. This enforces the presence of a + # category like heads/, tags/ etc. but the actual names are not restricted. + # If the --allow-onelevel option is used, this rule is waived. + # 4. They cannot have ASCII control characters (i.e. bytes whose values are + # lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere. + # 5. They cannot have question-mark ?, asterisk *, or open bracket [ anywhere. + - git tag "${S64_VERSION}--$(date +'%Y-%m-%dT%H.%M.%SZ' --utc)" + +deploy: + provider: releases + api_key: + secure: qzaOZ4K7QvEmSoij71PYqgQlxK/AtN9YelFVJwQzqt8Q8cxqqccXtoSciPQHN2nZEVUb2C7qk7bN93vkxL7Id8eN9FpBGx96kqlJqkYZDGJgGKTr+Fq4XuXjZjO2S9qpOJCDXpGOTKJE9qnMV7j08ax1u9OGAw7zeUfRjd1LgIAJEpFLzWGm3GTYy0hLtfrxtD4hodkLaIkA9azgWaKJWwRf0gqc25Ev8PElMGuYov7LtGNmb9V7hpVAqa55vSq32dvVreAoqomfxShmKjwpWP9P2fwxgc/F8XPFcySmxqwGWXfvrxoViQmVqfdW0ZKaBHm1VZkcfc9O6NL90AS2KpNYIn/fgigdPErxqfXP2GFWLVCuHmWVOhU+FoQjq8+sDnvbhOot1zE8NJD/7/HzBYnYK1N1gaf35gXfHHvi5tmnBC2tJ9LMiBk5i51Bgv+hPHLaOo6uYOddVxbOhGOQjek+zs4JmJ3Mqg0ACFC8BvEb8/rrd0qyP4DG+5btpQeDWMGADR9dApdekQnd7UIar9upiQnIQ/UuqKKDsbsx3xQPK1cjfXUbour5PkFe1ckkrGK1S5dDd7ZT2ql/u7GhWZw8W6SOOyTYdeYzW9awqciJQCcg4Sm1xnB4g2KuVxQLHGu1UMzFgb6n7al3i6twwcamVNTvZsjVj7Kbuz6JJnw= + file: + - "/home/travis/build/boramalper/sqlite3-x64/sqlite-src/sqldiff" + - "/home/travis/build/boramalper/sqlite3-x64/sqlite-src/sqlite3" + - "/home/travis/build/boramalper/sqlite3-x64/sqlite-src/sqlite3_analyzer" + - "/home/travis/build/boramalper/sqlite3-x64/sqlite-src/CHECKSUMS.sha1" + - "/home/travis/build/boramalper/sqlite3-x64/sqlite-src/sqlite-tools-linux-x64-${S64_VERSION}.zip" + - "/home/travis/build/boramalper/sqlite3-x64/sqlite-src/sqlite-tools-linux-x64-${S64_VERSION}.zip.sha1" + skip_cleanup: true + on: + repo: "boramalper/sqlite3-x64" diff --git a/src/desktop/bin/sqlite/linux64/build/README.md b/src/desktop/bin/sqlite/linux64/build/README.md new file mode 100644 index 0000000..b4a8b47 --- /dev/null +++ b/src/desktop/bin/sqlite/linux64/build/README.md @@ -0,0 +1,68 @@ +# sqlite3-x64 +As SQLite doesn't provide 64-bit versions of its precompiled binaries, I have +decided to provide them myself since building those tools (`sqldiff`, `sqlite3`, +and `sqlite3_analyzer`) can be quite time-consuming, and wildly inconvenient +when you are trying to access your database for a quick query... + +[**RELEASES**](https://github.com/boramalper/sqlite3-x64/releases) + +- **`sqldiff`** +- **`sqlite3`** +- **`sqlite3_analyzer`** +- **`CHECKSUMS.sha1`** + + SHA-1 checksums of the binaries `sqldiff`, `sqlite3`, and `sqlite3_analyzer`. +- **`sqlite-tools-linux-x64-.zip`** + + ZIP archive of the binaries `sqldiff`, `sqlite3`, and `sqlite3_analyzer`. +- **`sqlite-tools-linux-x64-.zip.sha1`** + + +## Building Process +1. This repository contains a file called `sqlite-src.url` which contains the + URL to the source code of the latest release of SQLite. + +2. Whenever changes are pushed to [the GitHub repository](https://github.com/boramalper/sqlite3-x64), + [Travis CI](https://travis-ci.org/boramalper/sqlite3-x64): + 1. Compiles the binaries and zips them + 2. Creates a new [release](https://github.com/boramalper/sqlite3-x64/releases) + 3. Deploys the binaries + +## Security +You might reasonably be concerned about whether the precompiled binaries are +tampered by me or not, for which you can check the `.travis.yml` file, and the +build log on [Travis CI](https://travis-ci.org/boramalper/sqlite3-x64) +which shows how the tools are compiled, and the SHA-1 sums of the SQLite source +zip (`sqlite-src-*.zip`), of the binaries, and of the final zip file +(`sqlite-tools-linux-x64-*.zip`). + +## Release Process +To release the precompiled binaries for the newest version of SQLite: + +1. Update `sqlite-src.url` with the URL of the source zip (`sqlite-src-*.zip`) + + ``` + https://www.sqlite.org/2018/sqlite-src-3240000.zip + ``` + +2. Update `sqlite-src.sha1` with the SHA1 checksum of the source zip. + + **Beware** of the formatting: SHA1 checksum, seperated by two spaces, and the + file name, on a single line + + ``` + fb558c49ee21a837713c4f1e7e413309aabdd9c7 sqlite-src-3240000.zip + ``` + +3. Update `env` with the version number: + + ``` + S64_VERSION=3240000 + ``` + +4. Commit changes, and push. + + **Beware** of your commit message: + + - All release commit messages must be of the format `[release] `. + - For all other changes, commit messages must be prefixed with `[other]`. diff --git a/src/desktop/bin/sqlite/linux64/build/env b/src/desktop/bin/sqlite/linux64/build/env new file mode 100644 index 0000000..eca0367 --- /dev/null +++ b/src/desktop/bin/sqlite/linux64/build/env @@ -0,0 +1 @@ +S64_VERSION=3260000 diff --git a/src/desktop/bin/sqlite/linux64/build/sqlite-src.sha1 b/src/desktop/bin/sqlite/linux64/build/sqlite-src.sha1 new file mode 100644 index 0000000..a538601 --- /dev/null +++ b/src/desktop/bin/sqlite/linux64/build/sqlite-src.sha1 @@ -0,0 +1 @@ +a05429d6a8337d60ddc7c6381b49941059a55f68 sqlite-src-3260000.zip diff --git a/src/desktop/bin/sqlite/linux64/build/sqlite-src.url b/src/desktop/bin/sqlite/linux64/build/sqlite-src.url new file mode 100644 index 0000000..7bb4a4d --- /dev/null +++ b/src/desktop/bin/sqlite/linux64/build/sqlite-src.url @@ -0,0 +1 @@ +https://www.sqlite.org/2018/sqlite-src-3260000.zip diff --git a/src/desktop/bin/sqlite/linux64/sqldiff b/src/desktop/bin/sqlite/linux64/sqldiff new file mode 100644 index 0000000..2bd2e55 Binary files /dev/null and b/src/desktop/bin/sqlite/linux64/sqldiff differ diff --git a/src/desktop/bin/sqlite/linux64/sqlite3 b/src/desktop/bin/sqlite/linux64/sqlite3 new file mode 100644 index 0000000..e907df2 Binary files /dev/null and b/src/desktop/bin/sqlite/linux64/sqlite3 differ diff --git a/src/desktop/bin/sqlite/linux64/sqlite3_analyzer b/src/desktop/bin/sqlite/linux64/sqlite3_analyzer new file mode 100644 index 0000000..72b057d Binary files /dev/null and b/src/desktop/bin/sqlite/linux64/sqlite3_analyzer differ diff --git a/src/desktop/bin/sqlite/osx/sqldiff b/src/desktop/bin/sqlite/osx/sqldiff new file mode 100644 index 0000000..e0006b1 Binary files /dev/null and b/src/desktop/bin/sqlite/osx/sqldiff differ diff --git a/src/desktop/bin/sqlite/osx/sqlite3 b/src/desktop/bin/sqlite/osx/sqlite3 new file mode 100644 index 0000000..d836a4b Binary files /dev/null and b/src/desktop/bin/sqlite/osx/sqlite3 differ diff --git a/src/desktop/bin/sqlite/osx/sqlite3_analyzer b/src/desktop/bin/sqlite/osx/sqlite3_analyzer new file mode 100644 index 0000000..47f0a34 Binary files /dev/null and b/src/desktop/bin/sqlite/osx/sqlite3_analyzer differ diff --git a/src/desktop/bin/sqlite/win32/sqldiff.exe b/src/desktop/bin/sqlite/win32/sqldiff.exe new file mode 100644 index 0000000..6024ca2 Binary files /dev/null and b/src/desktop/bin/sqlite/win32/sqldiff.exe differ diff --git a/src/desktop/bin/sqlite/win32/sqlite3.exe b/src/desktop/bin/sqlite/win32/sqlite3.exe new file mode 100644 index 0000000..df5ac5d Binary files /dev/null and b/src/desktop/bin/sqlite/win32/sqlite3.exe differ diff --git a/src/desktop/bin/sqlite/win32/sqlite3_analyzer.exe b/src/desktop/bin/sqlite/win32/sqlite3_analyzer.exe new file mode 100644 index 0000000..1d13589 Binary files /dev/null and b/src/desktop/bin/sqlite/win32/sqlite3_analyzer.exe differ diff --git a/src/desktop/db/cypherpoker.js.sqlite3 b/src/desktop/db/cypherpoker.js.sqlite3 new file mode 100644 index 0000000..e289e0a Binary files /dev/null and b/src/desktop/db/cypherpoker.js.sqlite3 differ diff --git a/src/desktop/main.js b/src/desktop/main.js new file mode 100644 index 0000000..c4c159e --- /dev/null +++ b/src/desktop/main.js @@ -0,0 +1,267 @@ +/** +* @file Entry point for CypherPoker.JS desktop (Electron) that launches bundled server and client processes. +* @version 0.4.0 +* @author Patrick Bay +* @copyright MIT License +*/ + +//JSDoc typedefs: +/** +* Application objects exposed to the loaded server context in addition to any +* already available objects. +* @typedef {Object} Exposed_Application_Objects +* @default { +* electronEnv:{@link electronEnv},
+* require:require,
+* Buffer:Buffer,
+* console:console,
+* module:module,
+* setInterval:setInterval,
+* clearInterval:clearInterval,
+* setTimeout:setTimeout,
+* clearTimeout:clearTimeout,
+* process:process +* } +*/ + +const {app, BrowserWindow, Menu} = require('electron'); +const vm = require('vm'); +const fs = require('fs'); + +/** +* @property {String} appVersion The version of the application. This information +* is appended to the {@link appTitle} and may be used in other places in the application. +*/ +const appVersion = "0.4.0"; +/** +* @property {String} appTitle The title of the application as it should appear in +* the main app window. This title may pre-pend additional information in any +* launched child windows. +*/ +const appTitle = "CypherPoker.JS v"+appVersion; +/** +* Electron application environment settings and references. Note that the values +* defined here may be overwritten at any time during runtime from their +* default values. +* @property {Object} electronEnv +* @property {Object} electronEnv.dir (Usually) relative directory references for +* the application. +* @property {String} electronEnv.dir.server="../server/" Directory containing the +* WebSocket Sessions server, accompanying API functionality, and data. +* @property {String} electronEnv.dir.client="../web/" Directory containing the +* web/browser client functionality and data. +* @property {String} electronEnv.dir.bin="./bin/" Directory containing additional +* platform-specific binaries used by CypherPoker.JS desktop. +* @property {Object} electronEnv.client Contains references and information +* about the web/browser client portion of the application. +* @property {BrowserWindow} electronEnv.client.host=null A reference to the main +* Electron {@link BrowserWindow} object created by {@link createClient} at +* startup. +* @property {Number} electronEnv.client.width=1024 The initial width of the main +* Electron {@link BrowserWindow} object. +* @property {Number} electronEnv.client.height=768 The initial height of the main +* Electron {@link BrowserWindow} object. +* @property {Object} electronEnv.server Contains references and information +* about the server portion of the application. +* @property {Exposed_Application_Objects} electronEnv.server.exposed_objects Objects exposed to +* (made available to), the execution context of the server. +* @property {Function} electronEnv.server.onInit=createClient The callback function to be +* invoked by the loaded server when it has fully initialized. +* @property {Object} electronEnv.database Contains settings for the database adapters that +* can be started using the {@link startDatabase} function. Each adapter's settings vary +* but each has at least a script path that specifies the adapter's script and +* an adapter reference that points to the virtual machine instance hosting the +* running script. +* @property {Number} electronEnv.host=this A reference to the main Electron +* process used to launch the server and client. +*/ +var electronEnv = { + dir: { + server:"../server/", + client:"../web/" + }, + client: { + host:null, + width: 1024, + height:768 + }, + server: { + exposed_objects: { + electronEnv:null, + require:require, + Buffer:Buffer, + console:console, + module:module, + setInterval:setInterval, + clearInterval:clearInterval, + setTimeout:setTimeout, + clearTimeout:clearTimeout, + process:process + }, + onInit:createClient + }, + database: { + sqlite3: { + adapter: null, + script: "./adapters/sqlite3.js", + bin: "./bin/sqlite/%os%/%bin%" + } + }, + host:this +} + +/** +* Handles any uncaught promise rejections (Node.js will terminate on +* uncaught rejections in future versions). +*/ +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Promise Rejection:', reason); + //application specific logging, throwing an error, or other logic here +}); + +/** +* Starts a database adapter and makes it available to the application. +* +* @param {String} dbAdapter The database adapter to start. The name must match one of those defined +* in the {@link electronEnv.database} objects. +*/ +async function startDatabase(dbAdapter) { + var adapterData = electronEnv.database[dbAdapter]; + var scriptPath = adapterData.script; + var adapterScript = fs.readFileSync(scriptPath, {encoding:"UTF-8"}); + var vmContext = new Object(); + vmContext = Object.assign(electronEnv.server.exposed_objects, vmContext); + var context = vm.createContext(vmContext); + vm.runInContext(adapterScript, context, { + displayErrors: true, + filename: scriptPath + }); + adapterData.adapter = context; + try { + var result = await context.initialize(adapterData); + return (true); + } catch (err) { + console.error ("Couldn't initialize \""+dbAdapter+"\" database adapter: \n"+err.stack); + return (false); + } +} + +/** +* Creates and initializes the main WebSocket Sessions server process that +* contains all of the non-client-bound (server) functionality. +* +* @return {Promise} The returned promise resolves with true if the +* server process was successfully started and initialized, otherwise an +* exception is thrown with a standard Error object. +* @async +*/ +async function createServer() { + electronEnv.server.exposed_objects.electronEnv = electronEnv; //add internal self-reference(!) + var scriptPath = electronEnv.dir.server+"server.js"; + var serverScript = fs.readFileSync(scriptPath, {encoding:"UTF-8"}); + var vmContext = new Object(); + vmContext = Object.assign(electronEnv.server.exposed_objects, vmContext); + var context = vm.createContext(vmContext); + vm.runInContext(serverScript, context, { + displayErrors: true, + filename: scriptPath + }); + return (true); +} + +/** +* Creates and initializes the main window process that contains all of +* the client-bound (web browser) functionality. +* +* @return {Promise} The returned promise resolves with true if the +* client process was successfully started and initialized, otherwise an +* exception is thrown with a standard Error object. +* @async +*/ +async function createClient() { + electronEnv.client.host = new BrowserWindow({ + width:electronEnv.client.width, + height:electronEnv.client.height, + nodeIntegration:true + }); + electronEnv.client.host.loadFile(electronEnv.dir.client+"index.html"); + electronEnv.client.host.setTitle(appTitle); //override the default window title + // If we want to open the DevTools: + electronEnv.client.host.webContents.openDevTools() + electronEnv.client.host.on('closed', onClientHostClosed); + return (true); +} + +/** +* Event listener invoked when the client host environment (Electron), is +* closed. +*/ +function onClientHostClosed() { + electronEnv.client.host = null; +} + +//Electron core event handlers and application startup: + +console.log ("Electron version: "+process.versions.electron); +console.log ("Chrome version: "+process.versions.chrome); +console.log ("Node.js version: "+process.versions.node); +console.log ("Host platform: "+process.platform+" ("+process.arch+")"); + +/** +* Function invoked when Electron has fully initialized, full API is available, and client windows +* may now be safely opened. +* +* @param {Object} launchInfo On macOS, launchInfo holds the userInfo of the NSUserNotification +* that was used to open the application, if it was launched from Notification Center. +*/ +function onAppReady(launchInfo) { + Menu.setApplicationMenu(null); //remove default menu + startDatabase("sqlite3").then(result => { + createServer(); + }).catch(err => { + console.error (err.stack); + }); +} + +/** +* (MacOS only) Various actions can trigger this event, such as launching the +* application for the first time, attempting to re-launch the application when +* it's already running, or clicking on the application's dock or taskbar icon. +*/ +function onAppActivate() { + Menu.setApplicationMenu(null); //remove default menu + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (mainWindow === null) { + startDatabase("sqlite3").then(result => { + createServer(); + }).catch(err => { + console.error (err.stack); + }); + } +} + +/** +* All child windows have been closed; application will terminate. +*/ +function onAppAllWindowsClosed() { + // On macOS it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + app.quit() + } +} + +/** +* Starts the application by binding core app events, setting initial data +* and references, and invoking any initialization functions. +*/ +function start() { + app.setName(appTitle); //override the default app name + app.on('ready', onAppReady); + app.on('window-all-closed', onAppAllWindowsClosed); + app.on('activate', onAppActivate); +} + +//start the application: +start(); diff --git a/src/desktop/package.json b/src/desktop/package.json new file mode 100644 index 0000000..5efd0e7 --- /dev/null +++ b/src/desktop/package.json @@ -0,0 +1,37 @@ +{ + "name": "CypherPokerJS-desktop", + "version": "0.4.0", + "description": "CypherPoker.JS standalone desktop (Electron) application", + "main": "main.js", + "scripts": { + "start": "electron .", + "postinstall": "node postinstall.js", + "doc": "jsdoc -r * -d ./docs/", + "docs": "jsdoc -r * -d ./docs/" + }, + "repository": "https://github.com/monicanagent", + "keywords": [ + "websocket", + "session", + "server", + "json-rpc", + "jsonrpc", + "2.0", + "http", + "ws", + "proxy" + ], + "author": "GitHub", + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.36", + "bip32": "^1.0.2", + "bip39": "^2.5.0", + "bitcoinjs-lib": "^4.0.1", + "electron": "^4.0.4", + "freedom-port-control": "^0.9.11", + "request": "^2.87.0", + "secp256k1": "^3.5.2", + "ws": "^5.2.0" + } +} diff --git a/src/desktop/postinstall.js b/src/desktop/postinstall.js new file mode 100644 index 0000000..00f3a7e --- /dev/null +++ b/src/desktop/postinstall.js @@ -0,0 +1,36 @@ +/** +* @file A post-install script for CypherPoker.JS desktop to patch installed modules for +* compatibility with Electron, apply any required file permissions, etc. +* +* @version 0.0.1 +*/ +const fs = require("fs"); + +console.log ("Patching installed modules for Electron compatibility..."); + +// Replace "rmd160" hash reference with "ripemd160" which is required as of Electron 4.0.4 because +// of switch from OpenSSL to BoringSSL): https://github.com/electron/electron/pull/16574 +console.log ("Patching bip32 library..."); +var patchScript = fs.readFileSync("./node_modules/bip32/crypto.js", {encoding:"UTF-8"}); +patchScript = patchScript.split("rmd160").join("ripemd160"); +fs.writeFileSync("./node_modules/bip32/crypto.js", patchScript, {encoding:"UTF-8"}); +console.log ("bip32 patched."); +console.log ("Patching bitcoinjs-lib library..."); +var patchScript = fs.readFileSync("./node_modules/bitcoinjs-lib/src/crypto.js", {encoding:"UTF-8"}); +patchScript = patchScript.split("rmd160").join("ripemd160"); +fs.writeFileSync("./node_modules/bitcoinjs-lib/src/crypto.js", patchScript, {encoding:"UTF-8"}); +console.log ("bitcoinjs-lib patched."); + +console.log ("All patches applied."); + +console.log ("Updating modes for binaries..."); +fs.chmodSync("./bin/sqlite/linux32/sqlite3", 755); +fs.chmodSync("./bin/sqlite/linux32/sqldiff", 755); +fs.chmodSync("./bin/sqlite/linux32/sqlite3_analyzer", 755); +fs.chmodSync("./bin/sqlite/linux64/sqlite3", 755); +fs.chmodSync("./bin/sqlite/linux64/sqldiff", 755); +fs.chmodSync("./bin/sqlite/linux64/sqlite3_analyzer", 755); +fs.chmodSync("./bin/sqlite/osx/sqlite3", 755); +fs.chmodSync("./bin/sqlite/osx/sqldiff", 755); +fs.chmodSync("./bin/sqlite/osx/sqlite3_analyzer", 755); +console.log ("Modes for binaries updated.");