You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
3.2 KiB

  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. module.exports = {
  24. getPass: getPass
  25. };
  26. const mod_tty = require('tty');
  27. const mod_fs = require('fs');
  28. const mod_assert = require('assert-plus');
  29. var BACKSPACE = String.fromCharCode(127);
  30. var CTRLC = '\u0003';
  31. var CTRLD = '\u0004';
  32. function getPass(opts, cb) {
  33. if (typeof (opts) === 'function' && cb === undefined) {
  34. cb = opts;
  35. opts = {};
  36. }
  37. mod_assert.object(opts, 'options');
  38. mod_assert.func(cb, 'callback');
  39. mod_assert.optionalString(opts.prompt, 'options.prompt');
  40. if (opts.prompt === undefined)
  41. opts.prompt = 'Password';
  42. openTTY(function (err, rfd, wfd, rtty, wtty) {
  43. if (err) {
  44. cb(err);
  45. return;
  46. }
  47. wtty.write(opts.prompt + ':');
  48. rtty.resume();
  49. rtty.setRawMode(true);
  50. rtty.resume();
  51. rtty.setEncoding('utf8');
  52. var pw = '';
  53. rtty.on('data', onData);
  54. function onData(data) {
  55. var str = data.toString('utf8');
  56. for (var i = 0; i < str.length; ++i) {
  57. var ch = str[i];
  58. switch (ch) {
  59. case '\r':
  60. case '\n':
  61. case CTRLD:
  62. cleanup();
  63. cb(null, pw);
  64. return;
  65. case CTRLC:
  66. cleanup();
  67. cb(new Error('Aborted'));
  68. return;
  69. case BACKSPACE:
  70. pw = pw.slice(0, pw.length - 1);
  71. break;
  72. default:
  73. pw += ch;
  74. break;
  75. }
  76. }
  77. }
  78. function cleanup() {
  79. wtty.write('\r\n');
  80. rtty.setRawMode(false);
  81. rtty.pause();
  82. rtty.removeListener('data', onData);
  83. if (wfd !== undefined && wfd !== rfd) {
  84. wtty.end();
  85. mod_fs.closeSync(wfd);
  86. }
  87. if (rfd !== undefined) {
  88. rtty.end();
  89. mod_fs.closeSync(rfd);
  90. }
  91. }
  92. });
  93. }
  94. function openTTY(cb) {
  95. mod_fs.open('/dev/tty', 'r+', function (err, rttyfd) {
  96. if ((err && (err.code === 'ENOENT' || err.code === 'EACCES')) ||
  97. (process.version.match(/^v0[.][0-8][.]/))) {
  98. cb(null, undefined, undefined, process.stdin,
  99. process.stdout);
  100. return;
  101. }
  102. var rtty = new mod_tty.ReadStream(rttyfd);
  103. mod_fs.open('/dev/tty', 'w+', function (err3, wttyfd) {
  104. var wtty = new mod_tty.WriteStream(wttyfd);
  105. if (err3) {
  106. cb(err3);
  107. return;
  108. }
  109. cb(null, rttyfd, wttyfd, rtty, wtty);
  110. });
  111. });
  112. }