index.js (3260B)
1 /* 2 * Copyright 2016, Joyent, Inc. All rights reserved. 3 * Author: Alex Wilson <alex.wilson@joyent.com> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to 7 * deal in the Software without restriction, including without limitation the 8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 * sell copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24 module.exports = { 25 getPass: getPass 26 }; 27 28 const mod_tty = require('tty'); 29 const mod_fs = require('fs'); 30 const mod_assert = require('assert-plus'); 31 32 var BACKSPACE = String.fromCharCode(127); 33 var CTRLC = '\u0003'; 34 var CTRLD = '\u0004'; 35 36 function getPass(opts, cb) { 37 if (typeof (opts) === 'function' && cb === undefined) { 38 cb = opts; 39 opts = {}; 40 } 41 mod_assert.object(opts, 'options'); 42 mod_assert.func(cb, 'callback'); 43 44 mod_assert.optionalString(opts.prompt, 'options.prompt'); 45 if (opts.prompt === undefined) 46 opts.prompt = 'Password'; 47 48 openTTY(function (err, rfd, wfd, rtty, wtty) { 49 if (err) { 50 cb(err); 51 return; 52 } 53 54 wtty.write(opts.prompt + ':'); 55 rtty.resume(); 56 rtty.setRawMode(true); 57 rtty.resume(); 58 rtty.setEncoding('utf8'); 59 60 var pw = ''; 61 rtty.on('data', onData); 62 63 function onData(data) { 64 var str = data.toString('utf8'); 65 for (var i = 0; i < str.length; ++i) { 66 var ch = str[i]; 67 switch (ch) { 68 case '\r': 69 case '\n': 70 case CTRLD: 71 cleanup(); 72 cb(null, pw); 73 return; 74 case CTRLC: 75 cleanup(); 76 cb(new Error('Aborted')); 77 return; 78 case BACKSPACE: 79 pw = pw.slice(0, pw.length - 1); 80 break; 81 default: 82 pw += ch; 83 break; 84 } 85 } 86 } 87 88 function cleanup() { 89 wtty.write('\r\n'); 90 rtty.setRawMode(false); 91 rtty.pause(); 92 rtty.removeListener('data', onData); 93 if (wfd !== undefined && wfd !== rfd) { 94 wtty.end(); 95 mod_fs.closeSync(wfd); 96 } 97 if (rfd !== undefined) { 98 rtty.end(); 99 mod_fs.closeSync(rfd); 100 } 101 } 102 }); 103 } 104 105 function openTTY(cb) { 106 mod_fs.open('/dev/tty', 'r+', function (err, rttyfd) { 107 if ((err && (err.code === 'ENOENT' || err.code === 'EACCES')) || 108 (process.version.match(/^v0[.][0-8][.]/))) { 109 cb(null, undefined, undefined, process.stdin, 110 process.stdout); 111 return; 112 } 113 var rtty = new mod_tty.ReadStream(rttyfd); 114 mod_fs.open('/dev/tty', 'w+', function (err3, wttyfd) { 115 var wtty = new mod_tty.WriteStream(wttyfd); 116 if (err3) { 117 cb(err3); 118 return; 119 } 120 cb(null, rttyfd, wttyfd, rtty, wtty); 121 }); 122 }); 123 }