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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. var Node = {
  2. child: require('child_process'),
  3. crypto: require('crypto'),
  4. fs: require('fs'),
  5. os: require('os'),
  6. path: require('path'),
  7. process: process,
  8. util: require('util')
  9. };
  10. function Attempt(instance, end) {
  11. var platform = Node.process.platform;
  12. if (platform === 'darwin') return Mac(instance, end);
  13. if (platform === 'linux') return Linux(instance, end);
  14. if (platform === 'win32') return Windows(instance, end);
  15. end(new Error('Platform not yet supported.'));
  16. }
  17. function EscapeDoubleQuotes(string) {
  18. if (typeof string !== 'string') throw new Error('Expected a string.');
  19. return string.replace(/"/g, '\\"');
  20. }
  21. function Exec() {
  22. if (arguments.length < 1 || arguments.length > 3) {
  23. throw new Error('Wrong number of arguments.');
  24. }
  25. var command = arguments[0];
  26. var options = {};
  27. var end = function() {};
  28. if (typeof command !== 'string') {
  29. throw new Error('Command should be a string.');
  30. }
  31. if (arguments.length === 2) {
  32. if (Node.util.isObject(arguments[1])) {
  33. options = arguments[1];
  34. } else if (Node.util.isFunction(arguments[1])) {
  35. end = arguments[1];
  36. } else {
  37. throw new Error('Expected options or callback.');
  38. }
  39. } else if (arguments.length === 3) {
  40. if (Node.util.isObject(arguments[1])) {
  41. options = arguments[1];
  42. } else {
  43. throw new Error('Expected options to be an object.');
  44. }
  45. if (Node.util.isFunction(arguments[2])) {
  46. end = arguments[2];
  47. } else {
  48. throw new Error('Expected callback to be a function.');
  49. }
  50. }
  51. if (/^sudo/i.test(command)) {
  52. return end(new Error('Command should not be prefixed with "sudo".'));
  53. }
  54. if (typeof options.name === 'undefined') {
  55. var title = Node.process.title;
  56. if (ValidName(title)) {
  57. options.name = title;
  58. } else {
  59. return end(new Error('process.title cannot be used as a valid name.'));
  60. }
  61. } else if (!ValidName(options.name)) {
  62. var error = '';
  63. error += 'options.name must be alphanumeric only ';
  64. error += '(spaces are allowed) and <= 70 characters.';
  65. return end(new Error(error));
  66. }
  67. if (typeof options.icns !== 'undefined') {
  68. if (typeof options.icns !== 'string') {
  69. return end(new Error('options.icns must be a string if provided.'));
  70. } else if (options.icns.trim().length === 0) {
  71. return end(new Error('options.icns must not be empty if provided.'));
  72. }
  73. }
  74. if (typeof options.env !== 'undefined') {
  75. if (typeof options.env !== 'object') {
  76. return end(new Error('options.env must be an object if provided.'));
  77. } else if (Object.keys(options.env).length === 0) {
  78. return end(new Error('options.env must not be empty if provided.'));
  79. } else {
  80. for (var key in options.env) {
  81. var value = options.env[key];
  82. if (typeof key !== 'string' || typeof value !== 'string') {
  83. return end(
  84. new Error('options.env environment variables must be strings.')
  85. );
  86. }
  87. // "Environment variable names used by the utilities in the Shell and
  88. // Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
  89. // letters, digits, and the '_' (underscore) from the characters defined
  90. // in Portable Character Set and do not begin with a digit. Other
  91. // characters may be permitted by an implementation; applications shall
  92. // tolerate the presence of such names."
  93. if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
  94. return end(
  95. new Error(
  96. 'options.env has an invalid environment variable name: ' +
  97. JSON.stringify(key)
  98. )
  99. );
  100. }
  101. if (/[\r\n]/.test(value)) {
  102. return end(
  103. new Error(
  104. 'options.env has an invalid environment variable value: ' +
  105. JSON.stringify(value)
  106. )
  107. );
  108. }
  109. }
  110. }
  111. }
  112. var platform = Node.process.platform;
  113. if (platform !== 'darwin' && platform !== 'linux' && platform !== 'win32') {
  114. return end(new Error('Platform not yet supported.'));
  115. }
  116. var instance = {
  117. command: command,
  118. options: options,
  119. uuid: undefined,
  120. path: undefined
  121. };
  122. Attempt(instance, end);
  123. }
  124. function Linux(instance, end) {
  125. LinuxBinary(instance,
  126. function(error, binary) {
  127. if (error) return end(error);
  128. var command = [];
  129. // Preserve current working directory:
  130. command.push('cd "' + EscapeDoubleQuotes(Node.process.cwd()) + '";');
  131. // Export environment variables:
  132. for (var key in instance.options.env) {
  133. var value = instance.options.env[key];
  134. command.push('export ' + key + '="' + EscapeDoubleQuotes(value) + '";');
  135. }
  136. command.push('"' + EscapeDoubleQuotes(binary) + '"');
  137. if (/kdesudo/i.test(binary)) {
  138. command.push(
  139. '--comment',
  140. '"' + instance.options.name + ' wants to make changes. ' +
  141. 'Enter your password to allow this."'
  142. );
  143. command.push('-d'); // Do not show the command to be run in the dialog.
  144. command.push('--');
  145. } else if (/pkexec/i.test(binary)) {
  146. command.push('--disable-internal-agent');
  147. }
  148. var magic = 'SUDOPROMPT\n';
  149. command.push(
  150. '/bin/bash -c "echo ' + EscapeDoubleQuotes(magic.trim()) + '; ' +
  151. EscapeDoubleQuotes(instance.command) +
  152. '"'
  153. );
  154. command = command.join(' ');
  155. Node.child.exec(command, { encoding: 'utf-8', maxBuffer: MAX_BUFFER },
  156. function(error, stdout, stderr) {
  157. // ISSUE 88:
  158. // We must distinguish between elevation errors and command errors.
  159. //
  160. // KDESUDO:
  161. // kdesudo provides no way to do this. We add a magic marker to know
  162. // if elevation succeeded. Any error thereafter is a command error.
  163. //
  164. // PKEXEC:
  165. // "Upon successful completion, the return value is the return value of
  166. // PROGRAM. If the calling process is not authorized or an
  167. // authorization could not be obtained through authentication or an
  168. // error occured, pkexec exits with a return value of 127. If the
  169. // authorization could not be obtained because the user dismissed the
  170. // authentication dialog, pkexec exits with a return value of 126."
  171. //
  172. // However, we do not rely on pkexec's return of 127 since our magic
  173. // marker is more reliable, and we already use it for kdesudo.
  174. var elevated = stdout && stdout.slice(0, magic.length) === magic;
  175. if (elevated) stdout = stdout.slice(magic.length);
  176. // Only normalize the error if it is definitely not a command error:
  177. // In other words, if we know that the command was never elevated.
  178. // We do not inspect error messages beyond NO_POLKIT_AGENT.
  179. // We cannot rely on English errors because of internationalization.
  180. if (error && !elevated) {
  181. if (/No authentication agent found/.test(stderr)) {
  182. error.message = NO_POLKIT_AGENT;
  183. } else {
  184. error.message = PERMISSION_DENIED;
  185. }
  186. }
  187. end(error, stdout, stderr);
  188. }
  189. );
  190. }
  191. );
  192. }
  193. function LinuxBinary(instance, end) {
  194. var index = 0;
  195. // We used to prefer gksudo over pkexec since it enabled a better prompt.
  196. // However, gksudo cannot run multiple commands concurrently.
  197. var paths = ['/usr/bin/kdesudo', '/usr/bin/pkexec'];
  198. function test() {
  199. if (index === paths.length) {
  200. return end(new Error('Unable to find pkexec or kdesudo.'));
  201. }
  202. var path = paths[index++];
  203. Node.fs.stat(path,
  204. function(error) {
  205. if (error) {
  206. if (error.code === 'ENOTDIR') return test();
  207. if (error.code === 'ENOENT') return test();
  208. end(error);
  209. } else {
  210. end(undefined, path);
  211. }
  212. }
  213. );
  214. }
  215. test();
  216. }
  217. function Mac(instance, callback) {
  218. var temp = Node.os.tmpdir();
  219. if (!temp) return callback(new Error('os.tmpdir() not defined.'));
  220. var user = Node.process.env.USER; // Applet shell scripts require $USER.
  221. if (!user) return callback(new Error('env[\'USER\'] not defined.'));
  222. UUID(instance,
  223. function(error, uuid) {
  224. if (error) return callback(error);
  225. instance.uuid = uuid;
  226. instance.path = Node.path.join(
  227. temp,
  228. instance.uuid,
  229. instance.options.name + '.app'
  230. );
  231. function end(error, stdout, stderr) {
  232. Remove(Node.path.dirname(instance.path),
  233. function(errorRemove) {
  234. if (error) return callback(error);
  235. if (errorRemove) return callback(errorRemove);
  236. callback(undefined, stdout, stderr);
  237. }
  238. );
  239. }
  240. MacApplet(instance,
  241. function(error, stdout, stderr) {
  242. if (error) return end(error, stdout, stderr);
  243. MacIcon(instance,
  244. function(error) {
  245. if (error) return end(error);
  246. MacPropertyList(instance,
  247. function(error, stdout, stderr) {
  248. if (error) return end(error, stdout, stderr);
  249. MacCommand(instance,
  250. function(error) {
  251. if (error) return end(error);
  252. MacOpen(instance,
  253. function(error, stdout, stderr) {
  254. if (error) return end(error, stdout, stderr);
  255. MacResult(instance, end);
  256. }
  257. );
  258. }
  259. );
  260. }
  261. );
  262. }
  263. );
  264. }
  265. );
  266. }
  267. );
  268. }
  269. function MacApplet(instance, end) {
  270. var parent = Node.path.dirname(instance.path);
  271. Node.fs.mkdir(parent,
  272. function(error) {
  273. if (error) return end(error);
  274. var zip = Node.path.join(parent, 'sudo-prompt-applet.zip');
  275. Node.fs.writeFile(zip, APPLET, 'base64',
  276. function(error) {
  277. if (error) return end(error);
  278. var command = [];
  279. command.push('/usr/bin/unzip');
  280. command.push('-o'); // Overwrite any existing applet.
  281. command.push('"' + EscapeDoubleQuotes(zip) + '"');
  282. command.push('-d "' + EscapeDoubleQuotes(instance.path) + '"');
  283. command = command.join(' ');
  284. Node.child.exec(command, { encoding: 'utf-8' }, end);
  285. }
  286. );
  287. }
  288. );
  289. }
  290. function MacCommand(instance, end) {
  291. var path = Node.path.join(
  292. instance.path,
  293. 'Contents',
  294. 'MacOS',
  295. 'sudo-prompt-command'
  296. );
  297. var script = [];
  298. // Preserve current working directory:
  299. // We do this for commands that rely on relative paths.
  300. // This runs in a subshell and will not change the cwd of sudo-prompt-script.
  301. script.push('cd "' + EscapeDoubleQuotes(Node.process.cwd()) + '"');
  302. // Export environment variables:
  303. for (var key in instance.options.env) {
  304. var value = instance.options.env[key];
  305. script.push('export ' + key + '="' + EscapeDoubleQuotes(value) + '"');
  306. }
  307. script.push(instance.command);
  308. script = script.join('\n');
  309. Node.fs.writeFile(path, script, 'utf-8', end);
  310. }
  311. function MacIcon(instance, end) {
  312. if (!instance.options.icns) return end();
  313. Node.fs.readFile(instance.options.icns,
  314. function(error, buffer) {
  315. if (error) return end(error);
  316. var icns = Node.path.join(
  317. instance.path,
  318. 'Contents',
  319. 'Resources',
  320. 'applet.icns'
  321. );
  322. Node.fs.writeFile(icns, buffer, end);
  323. }
  324. );
  325. }
  326. function MacOpen(instance, end) {
  327. // We must run the binary directly so that the cwd will apply.
  328. var binary = Node.path.join(instance.path, 'Contents', 'MacOS', 'applet');
  329. // We must set the cwd so that the AppleScript can find the shell scripts.
  330. var options = {
  331. cwd: Node.path.dirname(binary),
  332. encoding: 'utf-8'
  333. };
  334. // We use the relative path rather than the absolute path. The instance.path
  335. // may contain spaces which the cwd can handle, but which exec() cannot.
  336. Node.child.exec('./' + Node.path.basename(binary), options, end);
  337. }
  338. function MacPropertyList(instance, end) {
  339. // Value must be in single quotes (not double quotes) according to man entry.
  340. // e.g. defaults write com.companyname.appname "Default Color" '(255, 0, 0)'
  341. // The defaults command will be changed in an upcoming major release to only
  342. // operate on preferences domains. General plist manipulation utilities will
  343. // be folded into a different command-line program.
  344. var plist = Node.path.join(instance.path, 'Contents', 'Info.plist');
  345. var path = EscapeDoubleQuotes(plist);
  346. var key = EscapeDoubleQuotes('CFBundleName');
  347. var value = instance.options.name + ' Password Prompt';
  348. if (/'/.test(value)) {
  349. return end(new Error('Value should not contain single quotes.'));
  350. }
  351. var command = [];
  352. command.push('/usr/bin/defaults');
  353. command.push('write');
  354. command.push('"' + path + '"');
  355. command.push('"' + key + '"');
  356. command.push("'" + value + "'"); // We must use single quotes for value.
  357. command = command.join(' ');
  358. Node.child.exec(command, { encoding: 'utf-8' }, end);
  359. }
  360. function MacResult(instance, end) {
  361. var cwd = Node.path.join(instance.path, 'Contents', 'MacOS');
  362. Node.fs.readFile(Node.path.join(cwd, 'code'), 'utf-8',
  363. function(error, code) {
  364. if (error) {
  365. if (error.code === 'ENOENT') return end(new Error(PERMISSION_DENIED));
  366. end(error);
  367. } else {
  368. Node.fs.readFile(Node.path.join(cwd, 'stdout'), 'utf-8',
  369. function(error, stdout) {
  370. if (error) return end(error);
  371. Node.fs.readFile(Node.path.join(cwd, 'stderr'), 'utf-8',
  372. function(error, stderr) {
  373. if (error) return end(error);
  374. code = parseInt(code.trim(), 10); // Includes trailing newline.
  375. if (code === 0) {
  376. end(undefined, stdout, stderr);
  377. } else {
  378. error = new Error(
  379. 'Command failed: ' + instance.command + '\n' + stderr
  380. );
  381. error.code = code;
  382. end(error, stdout, stderr);
  383. }
  384. }
  385. );
  386. }
  387. );
  388. }
  389. }
  390. );
  391. }
  392. function Remove(path, end) {
  393. if (typeof path !== 'string' || !path.trim()) {
  394. return end(new Error('Argument path not defined.'));
  395. }
  396. var command = [];
  397. if (Node.process.platform === 'win32') {
  398. if (/"/.test(path)) {
  399. return end(new Error('Argument path cannot contain double-quotes.'));
  400. }
  401. command.push('rmdir /s /q "' + path + '"');
  402. } else {
  403. command.push('/bin/rm');
  404. command.push('-rf');
  405. command.push('"' + EscapeDoubleQuotes(Node.path.normalize(path)) + '"');
  406. }
  407. command = command.join(' ');
  408. Node.child.exec(command, { encoding: 'utf-8' }, end);
  409. }
  410. function UUID(instance, end) {
  411. Node.crypto.randomBytes(256,
  412. function(error, random) {
  413. if (error) random = Date.now() + '' + Math.random();
  414. var hash = Node.crypto.createHash('SHA256');
  415. hash.update('sudo-prompt-3');
  416. hash.update(instance.options.name);
  417. hash.update(instance.command);
  418. hash.update(random);
  419. var uuid = hash.digest('hex').slice(-32);
  420. if (!uuid || typeof uuid !== 'string' || uuid.length !== 32) {
  421. // This is critical to ensure we don't remove the wrong temp directory.
  422. return end(new Error('Expected a valid UUID.'));
  423. }
  424. end(undefined, uuid);
  425. }
  426. );
  427. }
  428. function ValidName(string) {
  429. // We use 70 characters as a limit to side-step any issues with Unicode
  430. // normalization form causing a 255 character string to exceed the fs limit.
  431. if (!/^[a-z0-9 ]+$/i.test(string)) return false;
  432. if (string.trim().length === 0) return false;
  433. if (string.length > 70) return false;
  434. return true;
  435. }
  436. function Windows(instance, callback) {
  437. var temp = Node.os.tmpdir();
  438. if (!temp) return callback(new Error('os.tmpdir() not defined.'));
  439. UUID(instance,
  440. function(error, uuid) {
  441. if (error) return callback(error);
  442. instance.uuid = uuid;
  443. instance.path = Node.path.join(temp, instance.uuid);
  444. if (/"/.test(instance.path)) {
  445. // We expect double quotes to be reserved on Windows.
  446. // Even so, we test for this and abort if they are present.
  447. return callback(
  448. new Error('instance.path cannot contain double-quotes.')
  449. );
  450. }
  451. instance.pathElevate = Node.path.join(instance.path, 'elevate.vbs');
  452. instance.pathExecute = Node.path.join(instance.path, 'execute.bat');
  453. instance.pathCommand = Node.path.join(instance.path, 'command.bat');
  454. instance.pathStdout = Node.path.join(instance.path, 'stdout');
  455. instance.pathStderr = Node.path.join(instance.path, 'stderr');
  456. instance.pathStatus = Node.path.join(instance.path, 'status');
  457. Node.fs.mkdir(instance.path,
  458. function(error) {
  459. if (error) return callback(error);
  460. function end(error, stdout, stderr) {
  461. Remove(instance.path,
  462. function(errorRemove) {
  463. if (error) return callback(error);
  464. if (errorRemove) return callback(errorRemove);
  465. callback(undefined, stdout, stderr);
  466. }
  467. );
  468. }
  469. WindowsWriteExecuteScript(instance,
  470. function(error) {
  471. if (error) return end(error);
  472. WindowsWriteCommandScript(instance,
  473. function(error) {
  474. if (error) return end(error);
  475. WindowsElevate(instance,
  476. function(error, stdout, stderr) {
  477. if (error) return end(error, stdout, stderr);
  478. WindowsWaitForStatus(instance,
  479. function(error) {
  480. if (error) return end(error);
  481. WindowsResult(instance, end);
  482. }
  483. );
  484. }
  485. );
  486. }
  487. );
  488. }
  489. );
  490. }
  491. );
  492. }
  493. );
  494. }
  495. function WindowsElevate(instance, end) {
  496. // We used to use this for executing elevate.vbs:
  497. // var command = 'cscript.exe //NoLogo "' + instance.pathElevate + '"';
  498. var command = [];
  499. command.push('powershell.exe');
  500. command.push('Start-Process');
  501. command.push('-FilePath');
  502. // Escape characters for cmd using double quotes:
  503. // Escape characters for PowerShell using single quotes:
  504. // Escape single quotes for PowerShell using backtick:
  505. // See: https://ss64.com/ps/syntax-esc.html
  506. command.push('"\'' + instance.pathExecute.replace(/'/g, "`'") + '\'"');
  507. command.push('-WindowStyle hidden');
  508. command.push('-Verb runAs');
  509. command = command.join(' ');
  510. var child = Node.child.exec(command, { encoding: 'utf-8' },
  511. function(error, stdout, stderr) {
  512. // We used to return PERMISSION_DENIED only for error messages containing
  513. // the string 'canceled by the user'. However, Windows internationalizes
  514. // error messages (issue 96) so now we must assume all errors here are
  515. // permission errors. This seems reasonable, given that we already run the
  516. // user's command in a subshell.
  517. if (error) return end(new Error(PERMISSION_DENIED), stdout, stderr);
  518. end();
  519. }
  520. );
  521. child.stdin.end(); // Otherwise PowerShell waits indefinitely on Windows 7.
  522. }
  523. function WindowsResult(instance, end) {
  524. Node.fs.readFile(instance.pathStatus, 'utf-8',
  525. function(error, code) {
  526. if (error) return end(error);
  527. Node.fs.readFile(instance.pathStdout, 'utf-8',
  528. function(error, stdout) {
  529. if (error) return end(error);
  530. Node.fs.readFile(instance.pathStderr, 'utf-8',
  531. function(error, stderr) {
  532. if (error) return end(error);
  533. code = parseInt(code.trim(), 10);
  534. if (code === 0) {
  535. end(undefined, stdout, stderr);
  536. } else {
  537. error = new Error(
  538. 'Command failed: ' + instance.command + '\r\n' + stderr
  539. );
  540. error.code = code;
  541. end(error, stdout, stderr);
  542. }
  543. }
  544. );
  545. }
  546. );
  547. }
  548. );
  549. }
  550. function WindowsWaitForStatus(instance, end) {
  551. // VBScript cannot wait for the elevated process to finish so we have to poll.
  552. // VBScript cannot return error code if user does not grant permission.
  553. // PowerShell can be used to elevate and wait on Windows 10.
  554. // PowerShell can be used to elevate on Windows 7 but it cannot wait.
  555. // powershell.exe Start-Process cmd.exe -Verb runAs -Wait
  556. Node.fs.stat(instance.pathStatus,
  557. function(error, stats) {
  558. if ((error && error.code === 'ENOENT') || stats.size < 2) {
  559. // Retry if file does not exist or is not finished writing.
  560. // We expect a file size of 2. That should cover at least "0\r".
  561. // We use a 1 second timeout to keep a light footprint for long-lived
  562. // sudo-prompt processes.
  563. setTimeout(
  564. function() {
  565. // If administrator has no password and user clicks Yes, then
  566. // PowerShell returns no error and execute (and command) never runs.
  567. // We check that command output has been redirected to stdout file:
  568. Node.fs.stat(instance.pathStdout,
  569. function(error) {
  570. if (error) return end(new Error(PERMISSION_DENIED));
  571. WindowsWaitForStatus(instance, end);
  572. }
  573. );
  574. },
  575. 1000
  576. );
  577. } else if (error) {
  578. end(error);
  579. } else {
  580. end();
  581. }
  582. }
  583. );
  584. }
  585. function WindowsWriteCommandScript(instance, end) {
  586. var cwd = Node.process.cwd();
  587. if (/"/.test(cwd)) {
  588. // We expect double quotes to be reserved on Windows.
  589. // Even so, we test for this and abort if they are present.
  590. return end(new Error('process.cwd() cannot contain double-quotes.'));
  591. }
  592. var script = [];
  593. script.push('@echo off');
  594. // Set code page to UTF-8:
  595. script.push('chcp 65001>nul');
  596. // Preserve current working directory:
  597. // We pass /d as an option in case the cwd is on another drive (issue 70).
  598. script.push('cd /d "' + cwd + '"');
  599. // Export environment variables:
  600. for (var key in instance.options.env) {
  601. // "The characters <, >, |, &, ^ are special command shell characters, and
  602. // they must be preceded by the escape character (^) or enclosed in
  603. // quotation marks. If you use quotation marks to enclose a string that
  604. // contains one of the special characters, the quotation marks are set as
  605. // part of the environment variable value."
  606. // In other words, Windows assigns everything that follows the equals sign
  607. // to the value of the variable, whereas Unix systems ignore double quotes.
  608. var value = instance.options.env[key];
  609. script.push('set ' + key + '=' + value.replace(/([<>\\|&^])/g, '^$1'));
  610. }
  611. script.push(instance.command);
  612. script = script.join('\r\n');
  613. Node.fs.writeFile(instance.pathCommand, script, 'utf-8', end);
  614. }
  615. function WindowsWriteElevateScript(instance, end) {
  616. // We do not use VBScript to elevate since it does not return an error if
  617. // the user does not grant permission. This is here for reference.
  618. // var script = [];
  619. // script.push('Set objShell = CreateObject("Shell.Application")');
  620. // script.push(
  621. // 'objShell.ShellExecute "' + instance.pathExecute + '", "", "", "runas", 0'
  622. // );
  623. // script = script.join('\r\n');
  624. // Node.fs.writeFile(instance.pathElevate, script, 'utf-8', end);
  625. }
  626. function WindowsWriteExecuteScript(instance, end) {
  627. var script = [];
  628. script.push('@echo off');
  629. script.push(
  630. 'call "' + instance.pathCommand + '"' +
  631. ' > "' + instance.pathStdout + '" 2> "' + instance.pathStderr + '"'
  632. );
  633. script.push('(echo %ERRORLEVEL%) > "' + instance.pathStatus + '"');
  634. script = script.join('\r\n');
  635. Node.fs.writeFile(instance.pathExecute, script, 'utf-8', end);
  636. }
  637. module.exports.exec = Exec;
  638. // We used to expect that applet.app would be included with this module.
  639. // This could not be copied when sudo-prompt was packaged within an asar file.
  640. // We now store applet.app as a zip file in base64 within index.js instead.
  641. // To recreate: "zip -r ../applet.zip Contents" (with applet.app as CWD).
  642. // The zip file must not include applet.app as the root directory so that we
  643. // can extract it directly to the target app directory.
  644. var APPLET = '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';
  645. var PERMISSION_DENIED = 'User did not grant permission.';
  646. var NO_POLKIT_AGENT = 'No polkit authentication agent found.';
  647. // See issue 66:
  648. var MAX_BUFFER = 134217728;