├── .travis.yml ├── README.md ├── config.js ├── host.js ├── install.sh ├── linux ├── ReadMe.txt ├── app │ ├── config.js │ ├── host.js │ ├── install.js │ └── messaging.js ├── install.sh ├── node │ ├── x64 │ │ └── node │ └── x86 │ │ └── node └── uninstall.sh ├── mac ├── ReadMe.txt ├── app │ ├── config.js │ ├── host.js │ ├── install.js │ └── messaging.js ├── install.sh ├── node │ └── x64 │ │ └── node └── uninstall.sh ├── messaging.js ├── prepare.sh └── windows ├── ReadMe.txt ├── app ├── Writting ├── config.js ├── host.js ├── install.js └── messaging.js ├── install.bat ├── node ├── x64 │ └── node.exe └── x86 │ └── node.exe └── uninstall.bat /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | script: 5 | - ./prepare.sh 6 | deploy: 7 | provider: releases 8 | prerelease: true 9 | api_key: 10 | secure: WLscrSf1ad9rEmZodg+tnqVo1IBwI5zAuaulC/bojiYmAR9pysFryas6SyGjgFCK9DKbFiaPUZXOWAT9q9uSgsPkWEARqE69BpXiDo4PLX0BVRhXd5C5Z0Neu6f03Zgx9lxY9/CalR3WF+qqrRSouUa2KveyMA1ofv6I+Q410do4qIbVWR5bY9RTU7SukdOQz1Nq4JhOHBNYEGSZEeu2/7kM8g3GJesSstOQaTSvOWSAQKeyj/aD63LfTNAd1zW577vyEjtXgz0xIvafot3WX88jarEJlLwNWciifssco6z1OCOOZ67unygmBiM62h35ism8qt2OX1S239my1L7kaoCNSPwbQ0GvP9foL9UkXl5zfIxUO6ZnkddouC+p5pI7DBSfJ9dJP3lToNJNei8jBa1xeLWByP6G82A5hKKrWBJEj4T2nXQUD5OAhy0cie0qeYHCA4bed4YMtpk3gHPXBkfhCfiq0qj9BOkPl+B0f0g1b6BGBvxfauphURH2QTqwckativGR4jIGipCHJdGA8WihRtHhoQgwppvlFiPsCJF/WaK2eTXpHAiIEZHNo28y3/frl1CBnpM5pFOfXJd2zk0BKQDyMR/0lcOXPrpaZyUt7lpkoVmCsJjIo+/Uf/fCt7vujol5b2YE4/MaedzJXhnXv3FdtyTot4A9FVOLUT4= 11 | file: 12 | - windows.zip 13 | - linux.zip 14 | - mac.zip 15 | on: 16 | tags: true 17 | overwrite: true 18 | skip_cleanup: true 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # native-client 2 | 3 | Native Client [![Build Status](https://travis-ci.org/andy-portmen/native-client.svg?branch=master)](https://travis-ci.org/andy-portmen/native-client) 4 | 5 | This [NodeJS](https://nodejs.org/) based small client helps the following extensions to communicate with your operation system. To see the latest log visit [travis-ci.org](https://travis-ci.org/andy-portmen/native-client). 6 | 7 | 1. Open in Firefox [open Firefox browser with provided URL] 8 | 1. Open in Google Chrome [open Google Chrome browser with provided URL] 9 | 2. Open in IE [open Internet Explorer browser with provided URL] 10 | 3. Open in Chrome [open Chrome browser with provided URL] 11 | 4. Open in Edge [open Microsoft Edge browser with provided URL] 12 | 5. Open in Safari [open Safari browser with provided URL] 13 | 6. Open in GIMP photo editor [open GIMP photo editor with provided URL or a temporary local image file (data-url's are being converted to a temporary local files and then GIMP is called to open this file)] 14 | 7. Open in VLC media Player [open VLC media Player with provided URL] 15 | 8. Media Converter and Muxer [Download FFmpeg media converter, Open FFmpeg, Export media files to a temporary directory then call FFmpeg] 16 | 17 | You can find up-to-date list as well as IDs here: https://github.com/andy-portmen/native-client/blob/master/config.js 18 | 19 | Notes: 20 | 21 | 1. On Linux and Mac, installer script only copies node executable if it is not already defined in the PATH environment. Please make sure you have an up-to-date version of NodeJS 22 | 2. On Linux and Mac, you can define custom root directory by adding `--custom-dir=` to the installer script 23 | Example: `./install.sh --custom-dir=~/Desktop/` 24 | 3. Removing the native client [Linux and Mac]: As of version 0.2.1, the installer prints all the directories it creates or inserts scripts in. Basically on Linux and Mac, two JSON files are inserted to predefined directories and a root directory is created which contains all the files. To remove the program simply delete the root directory and delete the two generated manifest JSON files. Path to all these files will be printed during installation 25 | 4. Removing the native client [windows]: On Windows OS, a directory is created in the "%LocalAPPData;" and all the files are inserted in this directory. To remove the program, simply delete this directory. Also note that two registry entries are also added so that Chrome, Opera, and Firefox browsers can find the actual executable. Path to these registry entries are also printed during installation. You can use "uninstall.bat" to remove all files and registries. 26 | 5. If you don't remember where the files are, simply run the installer one more time. It just overwrites all the files. 27 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.id = 'com.add0n.stylus'; 4 | exports.version = '0.1.4'; 5 | 6 | exports.ids = { 7 | chrome: [ 8 | 'mhjehpjceoajlmldfbelpnbapmcnabfe', // Stylus External Editor Integration (Chrome) 9 | 'djlnckfpakjlpfddhboajjmaeljfaacn', // Stylus External Editor Integration (Opera) 10 | ], 11 | firefox: [ 12 | '95fce6c2e847856344b2156491239d95f56c7fc1@temporary-addon', // Stylus External Editor Integration 13 | ] 14 | }; 15 | -------------------------------------------------------------------------------- /host.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var config = require('./config.js'); 4 | 5 | // closing node when parent process is killed 6 | process.stdin.resume(); 7 | process.stdin.on('end', () => process.exit()); 8 | 9 | function observe (request, push, done) { 10 | let close; 11 | const exception = e => { 12 | push({ 13 | type: 'exception', 14 | error: e.message 15 | }); 16 | close(); 17 | }; 18 | close = () => { 19 | process.removeListener('uncaughtException', exception); 20 | done(); 21 | close = () => {}; 22 | }; 23 | process.addListener('uncaughtException', exception); 24 | 25 | if (request.method === 'spec') { 26 | push({ 27 | version: config.version, 28 | env: process.env, 29 | release: process.release, 30 | platform: process.platform, 31 | arch: process.arch, 32 | versions: process.versions, 33 | separator: require('path').sep, 34 | tmpdir: require('os').tmpdir() 35 | }); 36 | close(); 37 | } 38 | else if ('script' in request) { 39 | const vm = require('vm'); 40 | const sandbox = { 41 | version: config.version, 42 | env: process.env, 43 | push, 44 | close, 45 | args: request.args, 46 | // only allow internal modules that extension already requested permission for 47 | require: (name) => (request.permissions || []).indexOf(name) === -1 ? null : require(name) 48 | }; 49 | const script = new vm.Script(request.script); 50 | const context = new vm.createContext(sandbox); 51 | script.runInContext(context); 52 | } 53 | else { 54 | push({ 55 | type: 'context', 56 | error: 'cannot find "script" key in your request. Closing connection...' 57 | }); 58 | close(); 59 | } 60 | } 61 | /* message passing */ 62 | var nativeMessage = require('./messaging'); 63 | process.stdin 64 | .pipe(new nativeMessage.Input()) 65 | .pipe(new nativeMessage.Transform(observe)) 66 | .pipe(new nativeMessage.Output()) 67 | .pipe(process.stdout); 68 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./app 4 | 5 | if type node 2>/dev/null; then 6 | echo "Installer is using your system NodeJS; Please make sure your NodeJS is up-to-date." 7 | node install.js `which node` $1 8 | else 9 | MACHINE_TYPE=`uname -m` 10 | echo "Installer is using the attached NodeJS" 11 | if [ ${MACHINE_TYPE} == 'x86_64' ]; then 12 | ../node/x64/node install.js --add_node $1 13 | else 14 | ../node/x86/node install.js --add_node $1 15 | fi 16 | fi 17 | -------------------------------------------------------------------------------- /linux/ReadMe.txt: -------------------------------------------------------------------------------- 1 | To install the native application 2 | 1. Open a terminal window and point it into this directory 3 | 2. run ./install.sh 4 | 5 | If you get "No such file or directory" message, it means the current directory is not the root of the extracted directory. Read http://add0n.com/open-in.html?#faq13 page for more info. 6 | 7 | Installation Guide: 8 | https://www.youtube.com/watch?v=bB4Bj_APg4g 9 | -------------------------------------------------------------------------------- /linux/app/config.js: -------------------------------------------------------------------------------- 1 | ../../config.js -------------------------------------------------------------------------------- /linux/app/host.js: -------------------------------------------------------------------------------- 1 | ../../host.js -------------------------------------------------------------------------------- /linux/app/install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | var share = process.argv.filter(a => a.startsWith('--custom-dir=')).map(a => a.split('=')[1])[0] || process.env.HOME; 7 | if (share[0] === '~') { 8 | share = path.join(process.env.HOME, share.slice(1)); 9 | } 10 | share = path.resolve(share); 11 | console.log(' -> Root directory is', share); 12 | 13 | function exists (directory, callback) { 14 | let root = '/'; 15 | let dirs = directory.split('/'); 16 | function one () { 17 | root = path.join(root, dirs.shift()); 18 | fs.stat(root, (e) => { 19 | if (!e && dirs.length) { 20 | one(); 21 | } 22 | else if (e && e.code === 'ENOENT') { 23 | fs.mkdir(root, (e) => { 24 | if (e) { 25 | callback(e); 26 | } 27 | else if (dirs.length) { 28 | one(); 29 | } 30 | else { 31 | callback(); 32 | } 33 | }); 34 | } 35 | else { 36 | callback(e); 37 | } 38 | }); 39 | } 40 | one(); 41 | } 42 | 43 | var {id, ids} = require('./config.js'); 44 | var dir = path.join(share, id); 45 | var name = id; 46 | 47 | function manifest (root, type, callback) { 48 | console.log(' -> Creating a directory at', root); 49 | exists(root, (e) => { 50 | if (e) { 51 | throw e; 52 | } 53 | let origins; 54 | if (type === 'chrome') { 55 | origins = '"allowed_origins": ' + JSON.stringify(ids.chrome.map(id => 'chrome-extension://' + id + '/')); 56 | } 57 | else { 58 | origins = '"allowed_extensions": ' + JSON.stringify(ids.firefox); 59 | } 60 | fs.writeFile(path.join(root, name + '.json'), `{ 61 | "name": "${name}", 62 | "description": "Node Host for Native Messaging", 63 | "path": "${path.join(dir, 'run.sh')}", 64 | "type": "stdio", 65 | ${origins} 66 | }`, (e) => { 67 | if (e) { 68 | throw e; 69 | } 70 | callback(); 71 | }); 72 | 73 | }); 74 | } 75 | function application (callback) { 76 | console.log(' -> Creating a directory at', dir); 77 | exists(dir, (e) => { 78 | if (e) { 79 | console.log('\x1b[31m', `-> You dont have permission to use "${share}" directory.` ,'\x1b[0m'); 80 | console.log('\x1b[31m', '-> Use custom directory instead. Example:' ,'\x1b[0m'); 81 | console.log('\x1b[31m', '-> ./install.sh --custom-dir=~/' ,'\x1b[0m'); 82 | 83 | throw e; 84 | } 85 | let isNode = process.argv.filter(a => a === '--add_node').length === 0; 86 | let run = isNode ? `#!/bin/bash\n${process.argv[2]} host.js` : '#!/bin/bash\n./node host.js'; 87 | fs.writeFile(path.join(dir, 'run.sh'), run, (e) => { 88 | if (e) { 89 | throw e; 90 | } 91 | fs.chmodSync(path.join(dir, 'run.sh'), '0755'); 92 | if (!isNode) { 93 | fs.createReadStream('../node').pipe(fs.createWriteStream(path.join(dir, 'node'))); 94 | fs.chmodSync(path.join(dir, 'node'), '0755'); 95 | } 96 | fs.createReadStream('host.js').pipe(fs.createWriteStream(path.join(dir, 'host.js'))); 97 | fs.createReadStream('config.js').pipe(fs.createWriteStream(path.join(dir, 'config.js'))); 98 | fs.createReadStream('messaging.js').pipe(fs.createWriteStream(path.join(dir, 'messaging.js'))); 99 | callback(); 100 | }); 101 | }); 102 | } 103 | function chrome (callback) { 104 | if (ids.chrome.length) { 105 | let loc = path.join( 106 | process.env.HOME, 107 | '.config/google-chrome/NativeMessagingHosts' 108 | ); 109 | manifest(loc, 'chrome', callback); 110 | console.error(' -> Chrome Browser is supported'); 111 | } 112 | else { 113 | callback(); 114 | } 115 | } 116 | function chromium (callback) { 117 | if (ids.chrome.length) { 118 | let loc = path.join( 119 | process.env.HOME, 120 | '.config/chromium/NativeMessagingHosts' 121 | ); 122 | manifest(loc, 'chrome', callback); 123 | console.error(' -> Chromium Browser is supported'); 124 | } 125 | else { 126 | callback(); 127 | } 128 | } 129 | function firefox (callback) { 130 | if (ids.firefox.length) { 131 | let loc = path.join( 132 | process.env.HOME, 133 | '.mozilla/native-messaging-hosts' 134 | ); 135 | manifest(loc, 'firefox', callback); 136 | console.error(' -> Firefox Browser is supported'); 137 | } 138 | else { 139 | callback(); 140 | } 141 | } 142 | chrome(() => chromium(() => firefox(() => { 143 | application(() => { 144 | console.error(' => Native Host is installed in', dir); 145 | console.error('\n\n>>> Application is ready to use <<<\n\n'); 146 | }); 147 | }))); 148 | -------------------------------------------------------------------------------- /linux/app/messaging.js: -------------------------------------------------------------------------------- 1 | ../../messaging.js -------------------------------------------------------------------------------- /linux/install.sh: -------------------------------------------------------------------------------- 1 | ../install.sh -------------------------------------------------------------------------------- /linux/node/x64/node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstyles/native-client/6780bd075f468101b1353940718c6c657a6943e3/linux/node/x64/node -------------------------------------------------------------------------------- /linux/node/x86/node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstyles/native-client/6780bd075f468101b1353940718c6c657a6943e3/linux/node/x86/node -------------------------------------------------------------------------------- /linux/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./app 4 | 5 | MACHINE_TYPE=`uname -m` 6 | if [ ${MACHINE_TYPE} == 'x86_64' ]; then 7 | id=`../node/x64/node -e "process.stdout.write(require('./config.js').id)"` 8 | else 9 | id=`../node/x86/node -e "process.stdout.write(require('./config.js').id)"` 10 | fi 11 | 12 | echo "Native Client id is \"${id}\"" 13 | echo 14 | 15 | echo " .. Removing manifest file for Google Chrome" 16 | rm ~/.config/google-chrome/NativeMessagingHosts/${id}.json 17 | echo " .. Removing manifest file for Chromium" 18 | rm ~/.config/chromium/NativeMessagingHosts/${id}.json 19 | echo " .. Removing manifest file for Mozilla Firefox" 20 | rm ~/.mozilla/native-messaging-hosts/${id}.json 21 | echo " .. Removing executable" 22 | rm -r ~/${id} 23 | 24 | echo 25 | echo ">>> Native Client is removed <<<". 26 | -------------------------------------------------------------------------------- /mac/ReadMe.txt: -------------------------------------------------------------------------------- 1 | To install the native application 2 | 1. Open a terminal window and point it into this directory 3 | 2. run ./install.sh 4 | 5 | If you get "No such file or directory" message, it means the current directory is not the root of the extracted directory. Read http://add0n.com/open-in.html?#faq13 page for more info. 6 | 7 | Installation Guide: 8 | https://www.youtube.com/watch?v=bB4Bj_APg4g 9 | -------------------------------------------------------------------------------- /mac/app/config.js: -------------------------------------------------------------------------------- 1 | ../../config.js -------------------------------------------------------------------------------- /mac/app/host.js: -------------------------------------------------------------------------------- 1 | ../../host.js -------------------------------------------------------------------------------- /mac/app/install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | var share = process.argv.filter(a => a.startsWith('--custom-dir=')).map(a => a.split('=')[1])[0] || process.env.HOME; 7 | if (share[0] === '~') { 8 | share = path.join(process.env.HOME, share.slice(1)); 9 | } 10 | share = path.resolve(share); 11 | console.log(' -> Root directory is', share); 12 | 13 | function exists (directory, callback) { 14 | let root = '/'; 15 | let dirs = directory.split('/'); 16 | function one () { 17 | root = path.join(root, dirs.shift()); 18 | fs.stat(root, (e) => { 19 | if (!e && dirs.length) { 20 | one(); 21 | } 22 | else if (e && e.code === 'ENOENT') { 23 | fs.mkdir(root, (e) => { 24 | if (e) { 25 | callback(e); 26 | } 27 | else if (dirs.length) { 28 | one(); 29 | } 30 | else { 31 | callback(); 32 | } 33 | }); 34 | } 35 | else { 36 | callback(e); 37 | } 38 | }); 39 | } 40 | one(); 41 | } 42 | 43 | var {id, ids} = require('./config.js'); 44 | var dir = path.join(share, id); 45 | var name = id; 46 | 47 | function manifest (root, type, callback) { 48 | console.log(' -> Creating a directory at', root); 49 | exists(root, (e) => { 50 | if (e) { 51 | throw e; 52 | } 53 | 54 | let origins; 55 | if (type === 'chrome') { 56 | origins = '"allowed_origins": ' + JSON.stringify(ids.chrome.map(id => 'chrome-extension://' + id + '/')); 57 | } 58 | else { 59 | origins = '"allowed_extensions": ' + JSON.stringify(ids.firefox); 60 | } 61 | fs.writeFile(path.join(root, name + '.json'), `{ 62 | "name": "${name}", 63 | "description": "Node Host for Native Messaging", 64 | "path": "${path.join(dir, 'run.sh')}", 65 | "type": "stdio", 66 | ${origins} 67 | }`, (e) => { 68 | if (e) { 69 | throw e; 70 | } 71 | callback(); 72 | }); 73 | }); 74 | } 75 | 76 | function application (callback) { 77 | console.log(' -> Creating a directory at', dir); 78 | exists(dir, (e) => { 79 | if (e) { 80 | console.log('\x1b[31m', `-> You dont have permission to use "${share}" directory.` ,'\x1b[0m'); 81 | console.log('\x1b[31m', '-> Use custom directory instead. Example:' ,'\x1b[0m'); 82 | console.log('\x1b[31m', '-> ./install.sh --custom-dir=~/' ,'\x1b[0m'); 83 | 84 | throw e; 85 | } 86 | 87 | let isNode = process.argv.filter(a => a === '--add_node').length === 0; 88 | let run = isNode ? `#!/bin/bash\n${process.argv[2]} host.js` : '#!/bin/bash\n./node host.js'; 89 | 90 | fs.writeFile(path.join(dir, 'run.sh'), run, (e) => { 91 | if (e) { 92 | throw e; 93 | } 94 | fs.chmodSync(path.join(dir, 'run.sh'), '0755'); 95 | if (!isNode) { 96 | fs.createReadStream(process.argv[0]).pipe(fs.createWriteStream(path.join(dir, 'node'))); 97 | fs.chmodSync(path.join(dir, 'node'), '0755'); 98 | } 99 | fs.createReadStream('host.js').pipe(fs.createWriteStream(path.join(dir, 'host.js'))); 100 | fs.createReadStream('config.js').pipe(fs.createWriteStream(path.join(dir, 'config.js'))); 101 | fs.createReadStream('messaging.js').pipe(fs.createWriteStream(path.join(dir, 'messaging.js'))); 102 | 103 | callback(); 104 | }); 105 | }); 106 | } 107 | 108 | function chrome (callback) { 109 | if (ids.chrome.length) { 110 | let loc = path.join( 111 | process.env.HOME, 112 | 'Library/Application Support/Google/Chrome/NativeMessagingHosts' 113 | ); 114 | manifest(loc, 'chrome', callback); 115 | console.error(' -> Chrome Browser is supported'); 116 | } 117 | else { 118 | callback(); 119 | } 120 | } 121 | function chromium (callback) { 122 | if (ids.chrome.length) { 123 | let loc = path.join( 124 | process.env.HOME, 125 | 'Library/Application Support/Chromium/NativeMessagingHosts' 126 | ); 127 | manifest(loc, 'chrome', callback); 128 | console.error(' -> Chromium Browser is supported'); 129 | } 130 | else { 131 | callback(); 132 | } 133 | } 134 | function firefox (callback) { 135 | if (ids.firefox.length) { 136 | let loc = path.join( 137 | process.env.HOME, 138 | 'Library/Application Support/Mozilla/NativeMessagingHosts' 139 | ); 140 | manifest(loc, 'firefox', callback); 141 | console.error(' -> Firefox Browser is supported'); 142 | } 143 | else { 144 | callback(); 145 | } 146 | } 147 | chrome(() => chromium(() => firefox(() => { 148 | application(() => { 149 | console.error(' -> Native Host is installed in', dir); 150 | console.error('\n\n>>> Application is ready to use <<<\n\n'); 151 | }); 152 | }))); 153 | -------------------------------------------------------------------------------- /mac/app/messaging.js: -------------------------------------------------------------------------------- 1 | ../../messaging.js -------------------------------------------------------------------------------- /mac/install.sh: -------------------------------------------------------------------------------- 1 | ../install.sh -------------------------------------------------------------------------------- /mac/node/x64/node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstyles/native-client/6780bd075f468101b1353940718c6c657a6943e3/mac/node/x64/node -------------------------------------------------------------------------------- /mac/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./app 4 | 5 | MACHINE_TYPE=`uname -m` 6 | if [ ${MACHINE_TYPE} == 'x86_64' ]; then 7 | id=`../node/x64/node -e "process.stdout.write(require('./config.js').id)"` 8 | else 9 | id=`../node/x86/node -e "process.stdout.write(require('./config.js').id)"` 10 | fi 11 | 12 | echo "Native Client id is \"${id}\"" 13 | echo 14 | 15 | echo " .. Removing manifest file for Google Chrome" 16 | rm ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/${id}.json 17 | echo " .. Removing manifest file for Chromium" 18 | rm ~/Library/Application\ Support/Chromium/NativeMessagingHosts/${id}.json 19 | echo " .. Removing manifest file for Mozilla Firefox" 20 | rm ~/Library/Application\ Support/Mozilla/NativeMessagingHosts/${id}.json 21 | echo " .. Removing executable" 22 | rm -r ~/${id} 23 | 24 | echo 25 | echo ">>> Native Client is removed <<<". 26 | -------------------------------------------------------------------------------- /messaging.js: -------------------------------------------------------------------------------- 1 | // chrome-native-messaging module 2 | // 3 | // Defines three Transform streams: 4 | // 5 | // - Input - transform native messages to JavaScript objects 6 | // - Output - transform JavaScript objects to native messages 7 | // - Transform - transform message objects to reply objects 8 | // - Debug - transform JavaScript objects to lines of JSON (for debugging, obviously) 9 | 10 | var stream = require('stream'); 11 | var util = require('util'); 12 | 13 | function Input() { 14 | stream.Transform.call(this); 15 | 16 | // Transform bytes... 17 | this._writableState.objectMode = false; 18 | // ...into objects. 19 | this._readableState.objectMode = true; 20 | 21 | // Unparsed data. 22 | this.buf = new Buffer(0); 23 | // Parsed length. 24 | this.len = null; 25 | } 26 | 27 | util.inherits(Input, stream.Transform); 28 | 29 | Input.prototype._transform = function(chunk, encoding, done) { 30 | // Save this chunk. 31 | this.buf = Buffer.concat([ this.buf, chunk ]); 32 | 33 | var self = this; 34 | 35 | function parseBuf() { 36 | // Do we have a length yet? 37 | if (typeof self.len !== 'number') { 38 | // Nope. Do we have enough bytes for the length? 39 | if (self.buf.length >= 4) { 40 | // Yep. Parse the bytes. 41 | self.len = self.buf.readUInt32LE(0); 42 | // Remove the length bytes from the buffer. 43 | self.buf = self.buf.slice(4); 44 | } 45 | } 46 | 47 | // Do we have a length yet? (We may have just parsed it.) 48 | if (typeof self.len === 'number') { 49 | // Yep. Do we have enough bytes for the message? 50 | if (self.buf.length >= self.len) { 51 | // Yep. Slice off the bytes we need. 52 | var message = self.buf.slice(0, self.len); 53 | // Remove the bytes for the message from the buffer. 54 | self.buf = self.buf.slice(self.len); 55 | // Clear the length so we know we need to parse it again. 56 | self.len = null; 57 | // Parse the message bytes. 58 | var obj = JSON.parse(message.toString()); 59 | // Enqueue it for reading. 60 | self.push(obj); 61 | // We could have more messages in the buffer so check again. 62 | parseBuf(); 63 | } 64 | } 65 | } 66 | 67 | // Check for a parsable buffer (both length and message). 68 | parseBuf(); 69 | 70 | // We're done. 71 | done(); 72 | }; 73 | 74 | function Output() { 75 | stream.Transform.call(this); 76 | 77 | this._writableState.objectMode = true; 78 | this._readableState.objectMode = false; 79 | } 80 | 81 | util.inherits(Output, stream.Transform); 82 | 83 | Output.prototype._transform = function(chunk, encoding, done) { 84 | var len = new Buffer(4); 85 | var buf = new Buffer(JSON.stringify(chunk)); 86 | 87 | len.writeUInt32LE(buf.length, 0); 88 | 89 | this.push(len); 90 | this.push(buf); 91 | 92 | done(); 93 | }; 94 | 95 | function Transform(handler) { 96 | stream.Transform.call(this); 97 | 98 | this._writableState.objectMode = true; 99 | this._readableState.objectMode = true; 100 | 101 | this.handler = handler; 102 | } 103 | 104 | util.inherits(Transform, stream.Transform); 105 | 106 | Transform.prototype._transform = function(msg, encoding, done) { 107 | this.handler(msg, this.push.bind(this), done); 108 | }; 109 | 110 | function Debug() { 111 | stream.Transform.call(this); 112 | 113 | this._writableState.objectMode = true; 114 | this._readableState.objectMode = false; 115 | } 116 | 117 | util.inherits(Debug, stream.Transform); 118 | 119 | Debug.prototype._transform = function(chunk, encoding, done) { 120 | this.push(JSON.stringify(chunk) + '\n'); 121 | 122 | done(); 123 | }; 124 | 125 | exports.Input = Input; 126 | exports.Output = Output; 127 | exports.Transform = Transform; 128 | exports.Debug = Debug; 129 | -------------------------------------------------------------------------------- /prepare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd linux 4 | zip ../linux.zip -9 -r * -x "*.DS_Store" 5 | cd ../mac 6 | zip ../mac.zip -9 -r * -x "*.DS_Store" 7 | cd ../windows 8 | zip ../windows.zip -9 -r * -x "*.DS_Store" 9 | -------------------------------------------------------------------------------- /windows/ReadMe.txt: -------------------------------------------------------------------------------- 1 | To install the native application: 2 | 3 | 4 | 1. Extract this downloaded ZIP file in a local directory. 5 | 6 | 7 | 2. Double-click on "install.bat". 8 | 9 | 10 | 3. Wait for the script to display the successful message. 11 | 12 | 13 | To uninstall the native application: 14 | 15 | 16 | 1. Double-click on "uninstall.bat". 17 | 18 | 19 | 2. Wait for the script to display the successful message. 20 | 21 | 22 | 23 | Note: these scripts do not need administrator permission anymore! 24 | 25 | 26 | 27 | 28 | 29 | Installation Guide (skip the administrator permission part): 30 | 31 | 32 | https://www.youtube.com/watch?v=18jAqTXBiZA 33 | -------------------------------------------------------------------------------- /windows/app/Writting: -------------------------------------------------------------------------------- 1 | - to Chrome Registry 2 | -------------------------------------------------------------------------------- /windows/app/config.js: -------------------------------------------------------------------------------- 1 | ../../config.js -------------------------------------------------------------------------------- /windows/app/host.js: -------------------------------------------------------------------------------- 1 | ../../host.js -------------------------------------------------------------------------------- /windows/app/install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | function exists (directory, callback) { 7 | fs.stat(directory, (e) => { 8 | 9 | if (e && e.code === 'ENOENT') { 10 | fs.mkdir(directory, callback); 11 | } 12 | else { 13 | callback(e); 14 | } 15 | }); 16 | } 17 | 18 | var {id, ids} = require('./config.js'); 19 | var dir = path.join(process.argv[2], id); 20 | var name = id; 21 | 22 | var {exec} = require('child_process'); 23 | 24 | console.log('.. Writting to Chrome Registry'); 25 | console.log(`.. Key: HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${id}`); 26 | exec(`REG ADD "HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${id}" /ve /t REG_SZ /d "%LocalAPPData%\\${id}\\manifest-chrome.json" /f`); 27 | 28 | console.log(`.. Writting to Firefox Registry`); 29 | console.log(`.. Key: HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\${id}`); 30 | exec(`REG ADD "HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\${id}" /ve /t REG_SZ /d "%LocalAPPData%\\${id}\\manifest-firefox.json" /f`); 31 | 32 | function manifest (type, callback) { 33 | exists(dir, (e) => { 34 | if (e) { 35 | throw e; 36 | } 37 | let origins; 38 | if (type === 'chrome') { 39 | origins = '"allowed_origins": ' + JSON.stringify(ids.chrome.map(id => 'chrome-extension://' + id + '/')); 40 | } 41 | else { 42 | origins = '"allowed_extensions": ' + JSON.stringify(ids.firefox); 43 | } 44 | fs.writeFile(path.join(dir, 'manifest-' + type + '.json'), `{ 45 | "name": "${name}", 46 | "description": "Node Host for Native Messaging", 47 | "path": "run.bat", 48 | "type": "stdio", 49 | ${origins} 50 | }`, (e) => { 51 | if (e) { 52 | throw e; 53 | } 54 | callback(); 55 | }); 56 | }); 57 | } 58 | function application (callback) { 59 | fs.writeFile(path.join(dir, 'run.bat'), `@echo off\n\n"%~dp0node.exe" "%~dp0host.js"`, (e) => { 60 | if (e) { 61 | throw e; 62 | } 63 | fs.createReadStream('host.js').pipe(fs.createWriteStream(path.join(dir, 'host.js'))); 64 | fs.createReadStream('config.js').pipe(fs.createWriteStream(path.join(dir, 'config.js'))); 65 | fs.createReadStream('messaging.js').pipe(fs.createWriteStream(path.join(dir, 'messaging.js'))); 66 | try { 67 | fs.createReadStream(process.argv[0]).pipe(fs.createWriteStream(path.join(dir, 'node.exe'))); 68 | } catch (e) {} 69 | callback(); 70 | }); 71 | } 72 | 73 | function chrome (callback) { 74 | if (ids.chrome.length) { 75 | manifest('chrome', callback); 76 | console.error('.. Chrome Browser is supported'); 77 | } 78 | else { 79 | callback(); 80 | } 81 | } 82 | function firefox (callback) { 83 | if (ids.firefox.length) { 84 | manifest('firefox', callback); 85 | console.error('.. Firefox Browser is supported'); 86 | } 87 | else { 88 | callback(); 89 | } 90 | } 91 | chrome(() => firefox(() => { 92 | application(() => { 93 | console.error('.. Native Host is installed in', dir); 94 | console.error('\n\n>>> Application is ready to use <<<\n\n'); 95 | }); 96 | })); 97 | -------------------------------------------------------------------------------- /windows/app/messaging.js: -------------------------------------------------------------------------------- 1 | ../../messaging.js -------------------------------------------------------------------------------- /windows/install.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | pushd "%~dp0" 4 | CD app 5 | 6 | IF "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( 7 | ..\node\x64\node.exe install.js "%LocalAPPData%" 8 | ) ELSE ( 9 | ..\node\x86\node.exe install.js "%LocalAPPData%" 10 | ) 11 | 12 | PAUSE 13 | -------------------------------------------------------------------------------- /windows/node/x64/node.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstyles/native-client/6780bd075f468101b1353940718c6c657a6943e3/windows/node/x64/node.exe -------------------------------------------------------------------------------- /windows/node/x86/node.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstyles/native-client/6780bd075f468101b1353940718c6c657a6943e3/windows/node/x86/node.exe -------------------------------------------------------------------------------- /windows/uninstall.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | pushd "%~dp0" 4 | CD app 5 | 6 | IF "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( 7 | FOR /f %%i in ('..\node\x64\node.exe -e "process.stdout.write(require('./config.js').id)"') do SET id=%%i 8 | ) ELSE ( 9 | FOR /f %%i in ('..\node\x86\node.exe -e "process.stdout.write(require('./config.js').id)"') do SET id=%%i 10 | ) 11 | 12 | echo sss%id% 13 | 14 | echo .. Deleting Chrome Registry 15 | REG DELETE "HKCU\Software\Google\Chrome\NativeMessagingHosts\%id%" /f 16 | 17 | echo .. Deleting Firefox Registry 18 | for %%f in ("%LocalAPPData%") do SET SHORT_PATH=%%~sf 19 | REG DELETE "HKCU\SOFTWARE\Mozilla\NativeMessagingHosts\%id%" /f 20 | 21 | echo .. Deleting %id% 22 | RMDIR /Q /S "%LocalAPPData%\%id%" 23 | 24 | echo. 25 | echo ^>^>^> Native Client is removed ^<^<^< 26 | echo. 27 | pause 28 | 29 | --------------------------------------------------------------------------------