| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | #!/usr/bin/env node
 | 
					
						
							|  |  |  | (function () { | 
					
						
							| 
									
										
										
										
											2016-09-30 12:33:38 -04:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | var pkg = require('../package.json'); | 
					
						
							| 
									
										
										
										
											2018-06-14 02:39:34 -06:00
										 |  |  | var os = require('os'); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | //var url = require('url');
 | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  | var fs = require('fs'); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | var path = require('path'); | 
					
						
							|  |  |  | var http = require('http'); | 
					
						
							| 
									
										
										
										
											2018-06-27 21:35:14 -06:00
										 |  |  | //var https = require('https');
 | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | var YAML = require('js-yaml'); | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  | var TOML = require('toml'); | 
					
						
							| 
									
										
										
										
											2018-09-12 03:33:38 -06:00
										 |  |  | var TPLS = TOML.parse(fs.readFileSync(path.join(__dirname, "../lib/en-us.toml"), 'utf8')); | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  | /* | 
					
						
							|  |  |  | if ('function' !== typeof TOML.stringify) { | 
					
						
							|  |  |  |   TOML.stringify = require('json2toml'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | var recase = require('recase').create({}); | 
					
						
							|  |  |  | var camelCopy = recase.camelCopy.bind(recase); | 
					
						
							|  |  |  | //var snakeCopy = recase.snakeCopy.bind(recase);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-21 06:10:49 +00:00
										 |  |  | var urequest = require('@coolaj86/urequest'); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | var common = require('../lib/cli-common.js'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var argv = process.argv.slice(2); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-13 10:45:54 -06:00
										 |  |  | var argIndex = argv.indexOf('--config'); | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  | if (-1 === argIndex) { | 
					
						
							|  |  |  |   argIndex = argv.indexOf('-c'); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | var confpath; | 
					
						
							| 
									
										
										
										
											2018-06-13 10:45:54 -06:00
										 |  |  | var useTty; | 
					
						
							| 
									
										
										
										
											2018-06-14 02:39:34 -06:00
										 |  |  | var state = {}; | 
					
						
							| 
									
										
										
										
											2018-06-13 10:45:54 -06:00
										 |  |  | if (-1 === argIndex) { | 
					
						
							|  |  |  |   argIndex = argv.indexOf('-c'); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-06-13 10:45:54 -06:00
										 |  |  | if (-1 !== argIndex) { | 
					
						
							|  |  |  |   confpath = argv.splice(argIndex, 2)[1]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | argIndex = argv.indexOf('--tty'); | 
					
						
							|  |  |  | if (-1 !== argIndex) { | 
					
						
							|  |  |  |   useTty = argv.splice(argIndex, 1); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function help() { | 
					
						
							| 
									
										
										
										
											2018-09-22 23:35:13 -06:00
										 |  |  |   var keys = Object.keys(TPLS.help).filter(function (key) { | 
					
						
							|  |  |  |     return 'remote' !== key; | 
					
						
							| 
									
										
										
										
											2018-09-22 14:48:39 -06:00
										 |  |  |   }); | 
					
						
							|  |  |  |   var key = keys.filter(function (key) { | 
					
						
							|  |  |  |     return -1 !== process.argv.indexOf(key); | 
					
						
							| 
									
										
										
										
											2018-09-22 23:35:13 -06:00
										 |  |  |   })[0] || 'remote'; | 
					
						
							|  |  |  |   console.info(TPLS.help[key].replace(/{version}/g, pkg.version)); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-14 02:42:41 -06:00
										 |  |  | var verstr = [ pkg.name + ' remote v' + pkg.version ]; | 
					
						
							| 
									
										
										
										
											2018-06-13 10:45:54 -06:00
										 |  |  | if (!confpath) { | 
					
						
							| 
									
										
										
										
											2018-06-14 02:39:34 -06:00
										 |  |  |   confpath = path.join(os.homedir(), '.config/telebit/telebit.yml'); | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  |   verstr.push('(--config \'' + confpath.replace(new RegExp('^' + os.homedir()), '~') + '\')'); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-22 14:48:39 -06:00
										 |  |  | if ([ '-h', '--help', 'help' ].some(function (arg) { | 
					
						
							|  |  |  |   return -1 !== argv.indexOf(arg); | 
					
						
							|  |  |  | })) { | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |   help(); | 
					
						
							|  |  |  |   process.exit(0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | if (!confpath || /^--/.test(confpath)) { | 
					
						
							|  |  |  |   help(); | 
					
						
							|  |  |  |   process.exit(1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  | function askForConfig(state, mainCb) { | 
					
						
							| 
									
										
										
										
											2018-06-13 10:45:54 -06:00
										 |  |  |   var fs = require('fs'); | 
					
						
							| 
									
										
										
										
											2018-06-28 03:27:34 -06:00
										 |  |  |   var ttyname = '/dev/tty'; | 
					
						
							|  |  |  |   var stdin = useTty ? fs.createReadStream(ttyname, { | 
					
						
							| 
									
										
										
										
											2018-06-28 03:31:31 -06:00
										 |  |  |     fd: fs.openSync(ttyname, fs.constants.O_RDONLY | fs.constants.O_NOCTTY) | 
					
						
							| 
									
										
										
										
											2018-06-26 19:11:46 -06:00
										 |  |  |   }) : process.stdin; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |   var readline = require('readline'); | 
					
						
							|  |  |  |   var rl = readline.createInterface({ | 
					
						
							| 
									
										
										
										
											2018-06-13 10:45:54 -06:00
										 |  |  |     input: stdin | 
					
						
							|  |  |  |   , output: process.stdout | 
					
						
							| 
									
										
										
										
											2018-07-11 18:11:37 -06:00
										 |  |  |     // https://github.com/nodejs/node/issues/21771
 | 
					
						
							| 
									
										
										
										
											2018-06-13 12:54:27 -06:00
										 |  |  |     // https://github.com/nodejs/node/issues/21319
 | 
					
						
							| 
									
										
										
										
											2018-07-11 18:11:37 -06:00
										 |  |  |   , terminal: !/^win/i.test(os.platform()) && !useTty | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   state._useTty = useTty; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // NOTE: Use of setTimeout
 | 
					
						
							|  |  |  |   // We're using setTimeout just to make the user experience a little
 | 
					
						
							|  |  |  |   // nicer, as if we're doing something inbetween steps, so that it
 | 
					
						
							|  |  |  |   // is a smooth rather than jerky experience.
 | 
					
						
							|  |  |  |   // >= 300ms is long enough to become distracted and change focus (a full blink, time for an idea to form as a thought)
 | 
					
						
							|  |  |  |   // <= 100ms is shorter than normal human reaction time (ability to place events chronologically, which happened first)
 | 
					
						
							|  |  |  |   // ~ 150-250ms is the sweet spot for most humans (long enough to notice change and not be jarred, but stay on task)
 | 
					
						
							|  |  |  |   var firstSet = [ | 
					
						
							|  |  |  |     function askEmail(cb) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (state.config.email) { cb(); return; } | 
					
						
							| 
									
										
										
										
											2018-06-28 15:43:34 -06:00
										 |  |  |       //console.info("");
 | 
					
						
							| 
									
										
										
										
											2018-06-28 15:37:31 -06:00
										 |  |  |       console.info("Welcome!"); | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |       console.info(""); | 
					
						
							| 
									
										
										
										
											2018-06-28 15:37:31 -06:00
										 |  |  |       console.info("By using Telebit you agree to:"); | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |       console.info(""); | 
					
						
							| 
									
										
										
										
											2018-06-28 15:37:31 -06:00
										 |  |  |       console.info("    [x] Accept the Telebit™ terms of service"); | 
					
						
							|  |  |  |       console.info("    [x] Accept the Let's Encrypt™ terms of service"); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							| 
									
										
										
										
											2018-06-28 17:19:49 -06:00
										 |  |  |       console.info("Enter your email to agree and login/create your account:"); | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |       console.info(""); | 
					
						
							|  |  |  |       // TODO attempt to read email from npmrc or the like?
 | 
					
						
							|  |  |  |       rl.question('email: ', function (email) { | 
					
						
							| 
									
										
										
										
											2018-06-13 11:18:54 -06:00
										 |  |  |         email = /@/.test(email) && email.trim(); | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |         if (!email) { askEmail(cb); return; } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |         state.config.email = email.trim(); | 
					
						
							|  |  |  |         state.config.agreeTos = true; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |         console.info(""); | 
					
						
							|  |  |  |         setTimeout(cb, 250); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-15 22:45:42 +00:00
										 |  |  |   , function askRelay(cb) { | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |       function checkRelay(relay) { | 
					
						
							| 
									
										
										
										
											2018-06-15 22:45:42 +00:00
										 |  |  |         // TODO parse and check https://{{relay}}/.well-known/telebit.cloud/directives.json
 | 
					
						
							|  |  |  |         if (!relay) { relay = 'telebit.cloud'; } | 
					
						
							| 
									
										
										
										
											2018-06-16 01:47:32 +00:00
										 |  |  |         relay = relay.trim(); | 
					
						
							|  |  |  |         var urlstr = common.parseUrl(relay) + common.apiDirectory; | 
					
						
							| 
									
										
										
										
											2018-06-21 06:10:49 +00:00
										 |  |  |         urequest({ url: urlstr, json: true }, function (err, resp, body) { | 
					
						
							| 
									
										
										
										
											2018-06-16 01:11:02 +00:00
										 |  |  |           if (err) { | 
					
						
							|  |  |  |             console.error("[Network Error] Failed to retrieve '" + urlstr + "'"); | 
					
						
							| 
									
										
										
										
											2018-06-16 01:47:32 +00:00
										 |  |  |             console.error(err); | 
					
						
							| 
									
										
										
										
											2018-06-16 01:11:02 +00:00
										 |  |  |             askRelay(cb); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2018-06-21 18:56:36 +00:00
										 |  |  |           if (200 !== resp.statusCode || (Buffer.isBuffer(body) || 'object' !== typeof body) || !body.api_host) { | 
					
						
							| 
									
										
										
										
											2018-06-21 19:24:09 +00:00
										 |  |  |             console.warn("==================="); | 
					
						
							|  |  |  |             console.warn("      WARNING      "); | 
					
						
							|  |  |  |             console.warn("==================="); | 
					
						
							|  |  |  |             console.warn(""); | 
					
						
							|  |  |  |             console.warn("[" + resp.statusCode + "] '" + urlstr + "'"); | 
					
						
							|  |  |  |             console.warn("This server does not describe a current telebit version (but it may still work)."); | 
					
						
							|  |  |  |             console.warn(""); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:56:36 +00:00
										 |  |  |             console.warn(body); | 
					
						
							|  |  |  |           } else if (body && body.pair_request) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |             state._can_pair = true; | 
					
						
							| 
									
										
										
										
											2018-06-16 01:11:02 +00:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |           state.config.relay = relay; | 
					
						
							| 
									
										
										
										
											2018-06-16 01:11:02 +00:00
										 |  |  |           cb(); | 
					
						
							| 
									
										
										
										
											2018-06-15 22:45:42 +00:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-08 02:31:36 -06:00
										 |  |  |       if (state.config.relay) { checkRelay(state.config.relay); return; } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info("What relay will you be using? (press enter for default)"); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       rl.question('relay [default: telebit.cloud]: ', checkRelay); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , function checkRelay(cb) { | 
					
						
							| 
									
										
										
										
											2018-06-24 00:34:24 -06:00
										 |  |  |       nextSet = []; | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if ('telebit.cloud' !== state.config.relay) { | 
					
						
							| 
									
										
										
										
											2018-06-24 00:34:24 -06:00
										 |  |  |         nextSet = nextSet.concat(standardSet); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (!state._can_pair) { | 
					
						
							| 
									
										
										
										
											2018-06-24 00:34:24 -06:00
										 |  |  |         nextSet = nextSet.concat(fossSet); | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |       } | 
					
						
							|  |  |  |       cb(); | 
					
						
							| 
									
										
										
										
											2018-06-15 22:45:42 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |   ]; | 
					
						
							|  |  |  |   var standardSet = [ | 
					
						
							|  |  |  |     // There are questions that we need to aks in the CLI
 | 
					
						
							|  |  |  |     // if we can't guarantee that they are being asked in the web interface
 | 
					
						
							|  |  |  |     function askAgree(cb) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (state.config.agreeTos) { cb(); return; } | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info("Do you accept the terms of service for each and all of the following?"); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info("\tTelebit - End-to-End Encrypted Relay"); | 
					
						
							|  |  |  |       console.info("\tGreenlock - Automated HTTPS"); | 
					
						
							|  |  |  |       console.info("\tLet's Encrypt - TLS Certificates"); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info("Type 'y' or 'yes' to accept these Terms of Service."); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       rl.question('agree to all? [y/N]: ', function (resp) { | 
					
						
							|  |  |  |         resp = resp.trim(); | 
					
						
							|  |  |  |         if (!/^y(es)?$/i.test(resp) && 'true' !== resp) { | 
					
						
							|  |  |  |           throw new Error("You didn't accept the Terms of Service... not sure what to do..."); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |         state.config.agreeTos = true; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |         console.info(""); | 
					
						
							|  |  |  |         setTimeout(cb, 250); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |   , function askUpdates(cb) { | 
					
						
							|  |  |  |       // required means transactional, security alerts, mandatory updates
 | 
					
						
							| 
									
										
										
										
											2018-06-13 11:18:54 -06:00
										 |  |  |       var options = [ 'newsletter', 'important', 'required' ]; | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (-1 !== options.indexOf(state._updates)) { cb(); return; } | 
					
						
							| 
									
										
										
										
											2018-06-13 11:18:54 -06:00
										 |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info("What updates would you like to receive? (" + options.join(',') + ")"); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							| 
									
										
										
										
											2018-06-21 11:01:16 +00:00
										 |  |  |       rl.question('messages (default: important): ', function (updates) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |         state._updates = (updates || '').trim().toLowerCase(); | 
					
						
							|  |  |  |         if (!state._updates) { state._updates = 'important'; } | 
					
						
							|  |  |  |         if (-1 === options.indexOf(state._updates)) { askUpdates(cb); return; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ('newsletter' === state._updates) { | 
					
						
							|  |  |  |           state.config.newsletter = true; | 
					
						
							|  |  |  |           state.config.communityMember = true; | 
					
						
							|  |  |  |         } else if ('important' === state._updates) { | 
					
						
							|  |  |  |           state.config.communityMember = true; | 
					
						
							| 
									
										
										
										
											2018-06-13 11:18:54 -06:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         setTimeout(cb, 250); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |   , function askTelemetry(cb) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (state.config.telemetry) { cb(); return; } | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info("Contribute project telemetry data? (press enter for default [yes])"); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       rl.question('telemetry [Y/n]: ', function (telemetry) { | 
					
						
							|  |  |  |         if (!telemetry || /^y(es)?$/i.test(telemetry)) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |           state.config.telemetry = true; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |         setTimeout(cb, 250); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ]; | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |   var fossSet = [ | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |     function askTokenOrSecret(cb) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (state._can_pair || state.token || state.config.token | 
					
						
							|  |  |  |         || state.secret || state.config.secret) { cb(); return; } | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       console.info("What's your authorization for '" + state.config.relay + "'?"); | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |       console.info(""); | 
					
						
							|  |  |  |       // TODO check .well-known to learn supported token types
 | 
					
						
							|  |  |  |       console.info("Currently supported:"); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info("\tToken (JWT format)"); | 
					
						
							|  |  |  |       console.info("\tShared Secret (HMAC hex)"); | 
					
						
							|  |  |  |       //console.info("\tPrivate key (hex)");
 | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       rl.question('auth: ', function (resp) { | 
					
						
							|  |  |  |         var jwt = require('jsonwebtoken'); | 
					
						
							|  |  |  |         resp = (resp || '').trim(); | 
					
						
							|  |  |  |         try { | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |           jwt.decode(resp); | 
					
						
							|  |  |  |           state.config.token = resp; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |         } catch(e) { | 
					
						
							| 
									
										
										
										
											2018-06-21 19:24:09 +00:00
										 |  |  |           // is not jwt
 | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |         if (!state.config.token) { | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |           resp = resp.toLowerCase(); | 
					
						
							|  |  |  |           if (resp === Buffer.from(resp, 'hex').toString('hex')) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |             state.config.secret = resp; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |         if (!state.config.token && !state.config.secret) { | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |           askTokenOrSecret(cb); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         setTimeout(cb, 250); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , function askServernames(cb) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (!state.config.secret || state.config._servernames) { cb(); return; } | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info("What servername(s) will you be relaying here?"); | 
					
						
							|  |  |  |       console.info("(use a comma-separated list such as example.com,example.net)"); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       rl.question('domain(s): ', function (resp) { | 
					
						
							|  |  |  |         resp = (resp || '').trim().split(/,/g); | 
					
						
							|  |  |  |         if (!resp.length) { askServernames(); return; } | 
					
						
							|  |  |  |         // TODO validate the domains
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |         state.config._servernames = resp; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |         setTimeout(cb, 250); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , function askPorts(cb) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (!state.config.secret || state.config._ports) { cb(); return; } | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       console.info("What tcp port(s) will you be relaying here?"); | 
					
						
							|  |  |  |       console.info("(use a comma-separated list such as 2222,5050)"); | 
					
						
							|  |  |  |       console.info(""); | 
					
						
							|  |  |  |       rl.question('port(s) [default:none]: ', function (resp) { | 
					
						
							|  |  |  |         resp = (resp || '').trim().split(/,/g); | 
					
						
							|  |  |  |         if (!resp.length) { askPorts(); return; } | 
					
						
							|  |  |  |         // TODO validate the domains
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |         state.config._ports = resp; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |         setTimeout(cb, 250); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  |   var nextSet = firstSet; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function next() { | 
					
						
							|  |  |  |     var q = nextSet.shift(); | 
					
						
							| 
									
										
										
										
											2018-06-13 11:18:54 -06:00
										 |  |  |     if (!q) { | 
					
						
							| 
									
										
										
										
											2018-06-13 12:54:27 -06:00
										 |  |  |       // https://github.com/nodejs/node/issues/21319
 | 
					
						
							| 
									
										
										
										
											2018-06-28 20:35:58 -06:00
										 |  |  |       if (useTty) { try { stdin.push(null); } catch(e) { /*ignore*/ } } | 
					
						
							| 
									
										
										
										
											2018-06-13 13:38:59 -06:00
										 |  |  |       rl.close(); | 
					
						
							| 
									
										
										
										
											2018-06-24 02:53:29 -06:00
										 |  |  |       if (useTty) { try { stdin.close(); } catch(e) { /*ignore*/ } } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       mainCb(null, state); | 
					
						
							| 
									
										
										
										
											2018-06-13 11:18:54 -06:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |     q(next); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   next(); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | var utils = { | 
					
						
							| 
									
										
										
										
											2018-06-27 21:35:14 -06:00
										 |  |  |   request: function request(opts, fn) { | 
					
						
							|  |  |  |     if (!opts) { opts = {}; } | 
					
						
							|  |  |  |     var service = opts.service || 'config'; | 
					
						
							| 
									
										
										
										
											2018-06-29 15:53:55 -06:00
										 |  |  |     var req = http.request({ | 
					
						
							| 
									
										
										
										
											2018-06-27 21:35:14 -06:00
										 |  |  |       socketPath: state._ipc.path | 
					
						
							|  |  |  |     , method: opts.method || 'GET' | 
					
						
							|  |  |  |     , path: '/rpc/' + service | 
					
						
							|  |  |  |     }, function (resp) { | 
					
						
							|  |  |  |       var body = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       function finish() { | 
					
						
							|  |  |  |         if (200 !== resp.statusCode) { | 
					
						
							|  |  |  |           console.warn(resp.statusCode); | 
					
						
							|  |  |  |           console.warn(body || ('get' + service + ' failed')); | 
					
						
							|  |  |  |           //cb(new Error("not okay"), body);
 | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!body) { fn(null, null); return; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           body = JSON.parse(body); | 
					
						
							|  |  |  |         } catch(e) { | 
					
						
							|  |  |  |           // ignore
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         fn(null, body); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (resp.headers['content-length']) { | 
					
						
							|  |  |  |         resp.on('data', function (chunk) { | 
					
						
							|  |  |  |           body += chunk.toString(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         resp.on('end', function () { | 
					
						
							|  |  |  |           finish(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         finish(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     req.on('error', function (err) { | 
					
						
							|  |  |  |       // ENOENT - never started, cleanly exited last start, or creating socket at a different path
 | 
					
						
							|  |  |  |       // ECONNREFUSED - leftover socket just needs to be restarted
 | 
					
						
							|  |  |  |       if ('ENOENT' === err.code || 'ECONNREFUSED' === err.code) { | 
					
						
							|  |  |  |         if (opts._taketwo) { | 
					
						
							|  |  |  |           console.error("Either the telebit service was not already (and could not be started) or its socket could not be written to."); | 
					
						
							|  |  |  |           console.error(err); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         require('../usr/share/install-launcher.js').install({ env: process.env }, function (err) { | 
					
						
							|  |  |  |           if (err) { fn(err); return; } | 
					
						
							|  |  |  |           opts._taketwo = true; | 
					
						
							| 
									
										
										
										
											2018-09-14 03:51:58 -06:00
										 |  |  |           setTimeout(function () { | 
					
						
							|  |  |  |             utils.request(opts, fn); | 
					
						
							|  |  |  |           }, 2500); | 
					
						
							| 
									
										
										
										
											2018-06-27 21:35:14 -06:00
										 |  |  |         }); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if ('ENOTSOCK' === err.code) { | 
					
						
							|  |  |  |         console.error(err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       console.error(err); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-06-29 15:53:55 -06:00
										 |  |  |     req.end(); | 
					
						
							| 
									
										
										
										
											2018-06-27 21:35:14 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | , putConfig: function putConfig(service, args, fn) { | 
					
						
							| 
									
										
										
										
											2018-06-29 15:53:55 -06:00
										 |  |  |     var req = http.request({ | 
					
						
							| 
									
										
										
										
											2018-06-14 02:39:34 -06:00
										 |  |  |       socketPath: state._ipc.path | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |     , method: 'POST' | 
					
						
							| 
									
										
										
										
											2018-07-26 11:26:11 -06:00
										 |  |  |     , path: '/rpc/' + service + '?_body=' + encodeURIComponent(JSON.stringify(args)) | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |     }, function (resp) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       function finish() { | 
					
						
							| 
									
										
										
										
											2018-06-21 11:01:16 +00:00
										 |  |  |         if ('function' === typeof fn) { | 
					
						
							|  |  |  |           fn(null, resp); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-13 13:38:59 -06:00
										 |  |  |         console.info(""); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |         if (200 !== resp.statusCode) { | 
					
						
							|  |  |  |           console.warn("'" + service + "' may have failed." | 
					
						
							|  |  |  |            + " Consider peaking at the logs either with 'journalctl -xeu telebit' or /opt/telebit/var/log/error.log"); | 
					
						
							|  |  |  |           console.warn(resp.statusCode, body); | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |           //cb(new Error("not okay"), body);
 | 
					
						
							| 
									
										
										
										
											2018-06-13 13:12:38 -06:00
										 |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!body) { | 
					
						
							|  |  |  |           console.info("👌"); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           body = JSON.parse(body); | 
					
						
							|  |  |  |         } catch(e) { | 
					
						
							|  |  |  |           // ignore
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ("AWAIT_AUTH" === body.code) { | 
					
						
							|  |  |  |           console.info(body.message); | 
					
						
							|  |  |  |         } else if ("CONFIG" === body.code) { | 
					
						
							|  |  |  |           delete body.code; | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  |           //console.info(TOML.stringify(body));
 | 
					
						
							| 
									
										
										
										
											2018-06-13 13:52:02 -06:00
										 |  |  |           console.info(YAML.safeDump(body)); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2018-06-30 19:34:15 -06:00
										 |  |  |           if ('http' === body.module) { | 
					
						
							|  |  |  |             // TODO we'll support slingshot-ing in the future
 | 
					
						
							|  |  |  |             if (String(body.local) === String(parseInt(body.local, 10))) { | 
					
						
							| 
									
										
										
										
											2018-07-03 05:07:42 -06:00
										 |  |  |               console.info('> Forwarding https://' + body.remote + ' => localhost:' + body.local); | 
					
						
							| 
									
										
										
										
											2018-06-30 19:34:15 -06:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2018-07-03 05:07:42 -06:00
										 |  |  |               console.info('> Serving ' + body.local + ' as https://' + body.remote); | 
					
						
							| 
									
										
										
										
											2018-06-30 19:34:15 -06:00
										 |  |  |             } | 
					
						
							|  |  |  |           } else if ('tcp' === body.module) { | 
					
						
							| 
									
										
										
										
											2018-07-03 05:07:42 -06:00
										 |  |  |               console.info('> Forwarding ' + state.config.relay + ':' + body.remote + ' => localhost:' + body.local); | 
					
						
							| 
									
										
										
										
											2018-06-30 19:34:15 -06:00
										 |  |  |           } else if ('ssh' === body.module) { | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  |               //console.info('> Forwarding ' + state.config.relay + ' -p ' + JSON.stringify(body) + ' => localhost:' + body.local);
 | 
					
						
							| 
									
										
										
										
											2018-07-03 05:07:42 -06:00
										 |  |  |               console.info('> Forwarding ssh+https (openssl proxy) => localhost:' + body.local); | 
					
						
							| 
									
										
										
										
											2018-06-30 19:34:15 -06:00
										 |  |  |           } else { | 
					
						
							|  |  |  |             console.info(JSON.stringify(body, null, 2)); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2018-07-03 05:07:42 -06:00
										 |  |  |           console.info(); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var body = ''; | 
					
						
							|  |  |  |       if (resp.headers['content-length']) { | 
					
						
							|  |  |  |         resp.on('data', function (chunk) { | 
					
						
							|  |  |  |           body += chunk.toString(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         resp.on('end', function () { | 
					
						
							|  |  |  |           finish(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         finish(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     req.on('error', function (err) { | 
					
						
							| 
									
										
										
										
											2018-06-21 11:01:16 +00:00
										 |  |  |       console.error('Put Config Error:'); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |       console.error(err); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-06-29 15:53:55 -06:00
										 |  |  |     req.end(); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  | // Two styles:
 | 
					
						
							|  |  |  | //     http 3000
 | 
					
						
							|  |  |  | //     http modulename
 | 
					
						
							|  |  |  | function makeRpc(key) { | 
					
						
							|  |  |  |   if (key !== argv[0]) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   utils.putConfig(argv[0], argv.slice(1)); | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  | function packConfig(config) { | 
					
						
							|  |  |  |   return Object.keys(config).map(function (key) { | 
					
						
							|  |  |  |     var val = config[key]; | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |     if ('undefined' === val) { | 
					
						
							|  |  |  |       throw new Error("'undefined' used as a string value"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if ('undefined' === typeof val) { | 
					
						
							|  |  |  |       //console.warn('[DEBUG]', key, 'is present but undefined');
 | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     if (val && 'object' === typeof val && !Array.isArray(val)) { | 
					
						
							|  |  |  |       val = JSON.stringify(val); | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     return key + ':' + val; // converts arrays to strings with ,
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getToken(err, state) { | 
					
						
							|  |  |  |   if (err) { | 
					
						
							|  |  |  |     console.error("Error while initializing config [init]:"); | 
					
						
							|  |  |  |     throw err; | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-30 18:18:03 -06:00
										 |  |  |   state.relay = state.config.relay; | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   // { _otp, config: {} }
 | 
					
						
							|  |  |  |   common.api.token(state, { | 
					
						
							|  |  |  |     error: function (err/*, next*/) { | 
					
						
							|  |  |  |       console.error("[Error] common.api.token:"); | 
					
						
							|  |  |  |       console.error(err); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , directory: function (dir, next) { | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       //console.log('[directory] Telebit Relay Discovered:');
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       //console.log(dir);
 | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       state._apiDirectory = dir; | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       next(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , tunnelUrl: function (tunnelUrl, next) { | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       //console.log('[tunnelUrl] Telebit Relay Tunnel Socket:', tunnelUrl);
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       state.wss = tunnelUrl; | 
					
						
							|  |  |  |       next(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , requested: function (authReq, next) { | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       //console.log("[requested] Pairing Requested");
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       state.config._otp = state.config._otp = authReq.otp; | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (!state.config.token && state._can_pair) { | 
					
						
							|  |  |  |         console.info(""); | 
					
						
							|  |  |  |         console.info("=============================================="); | 
					
						
							|  |  |  |         console.info("                 Hey, Listen!                 "); | 
					
						
							|  |  |  |         console.info("=============================================="); | 
					
						
							|  |  |  |         console.info("                                              "); | 
					
						
							|  |  |  |         console.info("  GO CHECK YOUR EMAIL!                        "); | 
					
						
							|  |  |  |         console.info("                                              "); | 
					
						
							|  |  |  |         console.info("  DEVICE PAIR CODE:     0000                  ".replace(/0000/g, state.config._otp)); | 
					
						
							|  |  |  |         console.info("                                              "); | 
					
						
							|  |  |  |         console.info("=============================================="); | 
					
						
							|  |  |  |         console.info(""); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       next(); | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   , connect: function (pretoken, next) { | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       //console.log("[connect] Enabling Pairing Locally...");
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       state.config.pretoken = pretoken; | 
					
						
							|  |  |  |       state._connecting = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO use php-style object querification
 | 
					
						
							|  |  |  |       utils.putConfig('config', packConfig(state.config), function (err/*, body*/) { | 
					
						
							|  |  |  |         if (err) { | 
					
						
							|  |  |  |           state._error = err; | 
					
						
							|  |  |  |           console.error("Error while initializing config [connect]:"); | 
					
						
							|  |  |  |           console.error(err); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-06-29 15:53:55 -06:00
										 |  |  |         console.info("waiting..."); | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |         next(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , offer: function (token, next) { | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       //console.log("[offer] Pairing Enabled by Relay");
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       state.config.token = token; | 
					
						
							|  |  |  |       if (state._error) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       state._connecting = true; | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       try { | 
					
						
							| 
									
										
										
										
											2018-06-29 15:53:55 -06:00
										 |  |  |         require('jsonwebtoken').decode(token); | 
					
						
							|  |  |  |         //console.log(require('jsonwebtoken').decode(token));
 | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       } catch(e) { | 
					
						
							|  |  |  |         console.warn("[warning] could not decode token"); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       utils.putConfig('config', packConfig(state.config), function (err/*, body*/) { | 
					
						
							|  |  |  |         if (err) { | 
					
						
							|  |  |  |           state._error = err; | 
					
						
							|  |  |  |           console.error("Error while initializing config [offer]:"); | 
					
						
							|  |  |  |           console.error(err); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         //console.log("Pairing Enabled Locally");
 | 
					
						
							|  |  |  |         next(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , granted: function (_, next) { | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       //console.log("[grant] Pairing complete!");
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       next(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , end: function () { | 
					
						
							|  |  |  |       utils.putConfig('enable', [], function (err) { | 
					
						
							|  |  |  |         if (err) { console.error(err); return; } | 
					
						
							| 
									
										
										
										
											2018-06-30 18:18:03 -06:00
										 |  |  |         console.info("Success"); | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // workaround for https://github.com/nodejs/node/issues/21319
 | 
					
						
							|  |  |  |         if (state._useTty) { | 
					
						
							|  |  |  |           setTimeout(function () { | 
					
						
							| 
									
										
										
										
											2018-06-30 20:14:38 -06:00
										 |  |  |             console.info("Some fun things to try first:\n"); | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  |             console.info("    ~/telebit http ~/public"); | 
					
						
							| 
									
										
										
										
											2018-06-30 20:14:38 -06:00
										 |  |  |             console.info("    ~/telebit tcp 5050"); | 
					
						
							|  |  |  |             console.info("    ~/telebit ssh auto"); | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |             console.info(); | 
					
						
							|  |  |  |             console.info("Press any key to continue..."); | 
					
						
							|  |  |  |             console.info(); | 
					
						
							|  |  |  |             process.exit(0); | 
					
						
							|  |  |  |           }, 0.5 * 1000); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // end workaround
 | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |         parseCli(state); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function parseCli(/*state*/) { | 
					
						
							| 
									
										
										
										
											2018-07-05 22:33:05 -06:00
										 |  |  |   var special = [ | 
					
						
							|  |  |  |     'false', 'none', 'off', 'disable' | 
					
						
							|  |  |  |   , 'true', 'auto', 'on', 'enable' | 
					
						
							|  |  |  |   ]; | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   if (-1 !== argv.indexOf('init')) { | 
					
						
							|  |  |  |     utils.putConfig('list', []/*, function (err) { | 
					
						
							|  |  |  |     }*/); | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ([ 'ssh', 'http', 'tcp' ].some(function (key) { | 
					
						
							|  |  |  |     if (key !== argv[0]) { | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (argv[1]) { | 
					
						
							| 
									
										
										
										
											2018-07-05 22:33:05 -06:00
										 |  |  |       if (String(argv[1]) === String(parseInt(argv[1], 10))) { | 
					
						
							|  |  |  |         // looks like a port
 | 
					
						
							|  |  |  |         argv[1] = parseInt(argv[1], 10); | 
					
						
							|  |  |  |       } else if (/\/|\\/.test(argv[1])) { | 
					
						
							|  |  |  |         // looks like a path
 | 
					
						
							| 
									
										
										
										
											2018-07-05 22:13:56 -06:00
										 |  |  |         argv[1] = path.resolve(argv[1]); | 
					
						
							| 
									
										
										
										
											2018-07-27 22:30:58 -06:00
										 |  |  |         // TODO make a default assignment here
 | 
					
						
							| 
									
										
										
										
											2018-07-05 22:33:05 -06:00
										 |  |  |       } else if (-1 === special.indexOf(argv[1])) { | 
					
						
							|  |  |  |         console.error("Not sure what you meant by '" + argv[1] + "'."); | 
					
						
							|  |  |  |         console.error("Remember: paths should begin with ." + path.sep + ", like '." + path.sep + argv[1] + "'"); | 
					
						
							|  |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2018-07-05 22:13:56 -06:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |       utils.putConfig(argv[0], argv.slice(1)); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |       return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   })) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   if ([ 'status', 'enable', 'disable', 'restart', 'list', 'save' ].some(makeRpc)) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   help(); | 
					
						
							|  |  |  |   process.exit(11); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function handleConfig(err, config) { | 
					
						
							|  |  |  |   //console.log('CONFIG');
 | 
					
						
							|  |  |  |   //console.log(config);
 | 
					
						
							|  |  |  |   state.config = config; | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  |   var verstrd = [ pkg.name + ' daemon v' + state.config.version ]; | 
					
						
							|  |  |  |   if (state.config.version && state.config.version !== pkg.version) { | 
					
						
							|  |  |  |     console.info(verstr.join(' '), verstrd.join(' ')); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     console.info(verstr.join(' ')); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (err) { console.error(err); process.exit(101); return; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //
 | 
					
						
							|  |  |  |   // check for init first, before anything else
 | 
					
						
							|  |  |  |   // because it has arguments that may help in
 | 
					
						
							|  |  |  |   // the next steps
 | 
					
						
							|  |  |  |   //
 | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |   if (-1 !== argv.indexOf('init')) { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     parsers.init(argv, getToken); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   if (!state.config.relay || !state.config.token) { | 
					
						
							|  |  |  |     if (!state.config.relay) { | 
					
						
							|  |  |  |       state.config.relay = 'telebit.cloud'; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |     //console.log("question the user?", Date.now());
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     askForConfig(state, function (err, state) { | 
					
						
							|  |  |  |       // no errors actually get passed, so this is just future-proofing
 | 
					
						
							|  |  |  |       if (err) { throw err; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!state.config.token && state._can_pair) { | 
					
						
							|  |  |  |         state.config._otp = common.otp(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |       //console.log("done questioning:", Date.now());
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (!state.token && !state.config.token) { | 
					
						
							|  |  |  |         getToken(err, state); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         parseCli(state); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 14:51:56 -06:00
										 |  |  |   //console.log("no questioning:");
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   parseCli(state); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function parseConfig(err, text) { | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     state._clientConfig = JSON.parse(text || '{}'); | 
					
						
							|  |  |  |   } catch(e1) { | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       state._clientConfig = YAML.safeLoad(text || '{}'); | 
					
						
							|  |  |  |     } catch(e2) { | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  |       try { | 
					
						
							|  |  |  |         state._clientConfig = TOML.parse(text || ''); | 
					
						
							|  |  |  |       } catch(e3) { | 
					
						
							|  |  |  |         console.error(e1.message); | 
					
						
							|  |  |  |         console.error(e2.message); | 
					
						
							|  |  |  |         process.exit(1); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   state._clientConfig = camelCopy(state._clientConfig || {}) || {}; | 
					
						
							|  |  |  |   common._init( | 
					
						
							|  |  |  |     // make a default working dir and log dir
 | 
					
						
							|  |  |  |     state._clientConfig.root || path.join(os.homedir(), '.local/share/telebit') | 
					
						
							|  |  |  |   , (state._clientConfig.root && path.join(state._clientConfig.root, 'etc')) | 
					
						
							|  |  |  |       || path.resolve(common.DEFAULT_CONFIG_PATH, '..') | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   state._ipc = common.pipename(state._clientConfig, true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!Object.keys(state._clientConfig).length) { | 
					
						
							|  |  |  |     console.info('(' + state._ipc.comment + ": " + state._ipc.path + ')'); | 
					
						
							|  |  |  |     console.info(""); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ((err && 'ENOENT' === err.code) || !Object.keys(state._clientConfig).length) { | 
					
						
							|  |  |  |     if (!err || 'ENOENT' === err.code) { | 
					
						
							|  |  |  |       //console.warn("Empty config file. Run 'telebit init' to configure.\n");
 | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       console.warn("Couldn't load config:\n\n\t" + err.message + "\n"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   utils.request({ service: 'config' }, handleConfig); | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var parsers = { | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |   init: function (argv, parseCb) { | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |     var answers = {}; | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     var boolish = [ '--advanced' ]; | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |     if ('init' !== argv[0]) { | 
					
						
							|  |  |  |       throw new Error("init must be the first argument"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     argv.shift(); | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     // init --foo bar
 | 
					
						
							|  |  |  |     argv.forEach(function (arg, i) { | 
					
						
							|  |  |  |       if (!/^--/.test(arg)) { return; } | 
					
						
							|  |  |  |       if (-1 !== boolish.indexOf(arg)) { | 
					
						
							|  |  |  |         answers['_' + arg.replace(/^--/, '')] = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (/^-/.test(argv[i + 1])) { | 
					
						
							|  |  |  |         throw new Error(argv[i + 1] + ' requires an argument'); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       answers[arg] = argv[i + 1]; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // init foo:bar
 | 
					
						
							|  |  |  |     argv.forEach(function (arg) { | 
					
						
							|  |  |  |       if (/^--/.test(arg)) { return; } | 
					
						
							|  |  |  |       var parts = arg.split(/:/g); | 
					
						
							|  |  |  |       if (2 !== parts.length) { | 
					
						
							|  |  |  |         throw new Error("bad option to init: '" + arg + "'"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (answers[parts[0]]) { | 
					
						
							|  |  |  |         throw new Error("duplicate key to init '" + parts[0] + "'"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       answers[parts[0]] = parts[1]; | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-08 01:39:27 -06:00
										 |  |  |     if (answers.relay) { | 
					
						
							|  |  |  |       console.info("using --relay " + answers.relay); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     // things that aren't straight-forward copy-over
 | 
					
						
							|  |  |  |     if (!answers.advanced && !answers.relay) { | 
					
						
							|  |  |  |       answers.relay = 'telebit.cloud'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (Array.isArray(common._NOTIFICATIONS[answers.update])) { | 
					
						
							|  |  |  |       common._NOTIFICATIONS[answers.update].forEach(function (name) { | 
					
						
							|  |  |  |         state.config[name] = true; | 
					
						
							| 
									
										
										
										
											2018-06-27 21:35:14 -06:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (answers.servernames) { | 
					
						
							|  |  |  |       state.config._servernames = answers.servernames; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (answers.ports) { | 
					
						
							|  |  |  |       state.config._ports = answers.ports; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-27 21:35:14 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     // things that are straight-forward copy-over
 | 
					
						
							|  |  |  |     common.CONFIG_KEYS.forEach(function (key) { | 
					
						
							|  |  |  |       if ('true' === answers[key]) { answers[key] = true; } | 
					
						
							|  |  |  |       if ('false' === answers[key]) { answers[key] = false; } | 
					
						
							|  |  |  |       if ('null' === answers[key]) { answers[key] = null; } | 
					
						
							|  |  |  |       if ('undefined' === answers[key]) { delete answers[key]; } | 
					
						
							|  |  |  |       if ('undefined' !== typeof answers[key]) { | 
					
						
							|  |  |  |         state.config[key] = answers[key]; | 
					
						
							| 
									
										
										
										
											2018-06-21 19:24:09 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-06-21 11:01:16 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |     askForConfig(state, function (err, state) { | 
					
						
							|  |  |  |       if (err) { parseCb(err); return; } | 
					
						
							| 
									
										
										
										
											2018-06-27 21:35:14 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       if (!state.config.token && state._can_pair) { | 
					
						
							|  |  |  |         state.config._otp = common.otp(); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-06-27 21:35:14 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-30 18:18:03 -06:00
										 |  |  |       argv.unshift('init'); | 
					
						
							| 
									
										
										
										
											2018-06-29 04:15:23 -06:00
										 |  |  |       parseCb(null, state); | 
					
						
							| 
									
										
										
										
											2018-06-12 04:36:37 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-23 16:02:08 -06:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-11 02:03:50 -06:00
										 |  |  | fs.readFile(confpath, 'utf8', parseConfig); | 
					
						
							| 
									
										
										
										
											2018-06-11 14:52:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | }()); |