|
|
@@ -9,11 +9,24 @@ function elevatedRunIPCWriteMessage( target, m ) { |
|
|
|
fs.writeFileSync(target, ', ' + JSON.stringify( m ), { 'flag': 'a+' }) |
|
|
|
} |
|
|
|
|
|
|
|
function toHTML(l, x, r, ol, or){ |
|
|
|
ol = ol || '<div>' |
|
|
|
or = or || '</div>' |
|
|
|
l = l || '<div>' |
|
|
|
r = r || '</div>' |
|
|
|
if(Object.prototype.toString.call(x) === '[object Array]') { |
|
|
|
var ahtml = [] |
|
|
|
x.forEach(xi => { ahtml.push( `${l}${xi}${r}` ) }) |
|
|
|
return `${ol}${ahtml.join('')}${or}` |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var __isElevated = null; |
|
|
|
var shell_verse = { |
|
|
|
// getCommonTask is agnostic of whether we are running in an elevated shell or not. It runs in either case. |
|
|
|
getCommonTask( taskToRun ){ return ()=>{ return shell_verse.runTask(taskToRun) }} |
|
|
|
init( o ){ Object.assign(this, o) } |
|
|
|
, downloadsdir : '../Downloads' |
|
|
|
, getCommonTask( taskToRun ){ return ()=>{ return shell_verse.runTask(taskToRun) }} |
|
|
|
, runTask : ( taskToRun ) => { |
|
|
|
if (__isElevated) return shell_verse.elevatedRunner( taskToRun ) |
|
|
|
else return shell_verse.runNonElevated( taskToRun ) |
|
|
@@ -225,9 +238,291 @@ var shell_verse = { |
|
|
|
console.error('Run log error probably was not created by runas : ' + e) |
|
|
|
} |
|
|
|
}) |
|
|
|
.catch(err => console.error('Elevation failed : ' + err)); |
|
|
|
.catch(err => console.error('Elevation failed : ' + err)); |
|
|
|
}) |
|
|
|
} |
|
|
|
, launchui() { |
|
|
|
// PB : TODO -- Multiple parallel requests should be queued into a batch and serialized as a single promise. |
|
|
|
// PB : TODO -- cleanup elevated execution code from here. This is a non elevated hta. |
|
|
|
|
|
|
|
var processedArgs = this.processedArgs |
|
|
|
// Wait for the runas to complete before we read it. |
|
|
|
try { |
|
|
|
fs.unlinkSync('run.done') // Need a unique file for aech elevated run. |
|
|
|
} |
|
|
|
catch (e) { } //Ignore |
|
|
|
|
|
|
|
console.log(this.processedArgs.awaiturn) |
|
|
|
|
|
|
|
// Find node path to send to hta. |
|
|
|
return nodeShellExec('where', ['node']).then(r => { |
|
|
|
var namedArgs = []; |
|
|
|
console.log('result : ' + JSON.stringify(r)) |
|
|
|
Object.keys(processedArgs).forEach((v) => { v != '_' ? namedArgs.push('--' + v + '=' + processedArgs[v]) : null; }) |
|
|
|
// PB : TODO -- Convert all the cli args back to string. |
|
|
|
var _args = [] |
|
|
|
if(processedArgs._[0] === 'launchui' ) { _args = processedArgs._.slice(1); _args.splice(0,0, 'pull') } // Default command. |
|
|
|
else _args = processedArgs._; |
|
|
|
var args = [ path.normalize(`${this.selectedinstance.root}/.elxr/run-${this.runtimestamp}/windowselevate.hta`) ].concat(_args) |
|
|
|
namedArgs.length > 0 ? args = args.concat(namedArgs.join(' ')) : null; |
|
|
|
// args.push('--runas=self'); |
|
|
|
var spawntimestamp = (new Date()).getTime() |
|
|
|
args.push(`--runtimestamp=${spawntimestamp}`); |
|
|
|
|
|
|
|
// args.push('--nodepath=' + r.messages[r.messages.length - 1]) |
|
|
|
// if (!processedArgs.node_env) args.push('--node_env=' + ENV.NODE_ENV) |
|
|
|
// if (processedArgs.debug) args.push('--debug=true') // Enable to debug elevated.. |
|
|
|
// console.dir(processedArgs._) |
|
|
|
// console.dir(namedArgs.join(' ')) |
|
|
|
console.dir(args) |
|
|
|
// throw 'test' |
|
|
|
|
|
|
|
return nodeShellExec('MSHTA', [`"${args.join('" "')}"`] |
|
|
|
, { |
|
|
|
inherit: true |
|
|
|
, shell: true |
|
|
|
, env: this.ENV |
|
|
|
// , runas: 'self' |
|
|
|
// , title: `runas` |
|
|
|
} |
|
|
|
).then(() => { |
|
|
|
// runas returned. |
|
|
|
try { |
|
|
|
// PB : TODO -- Log is comma prefixed. Needs to be proper JSON. |
|
|
|
var runlogjson = `${this.selectedinstance.root}/.elxr/run-${spawntimestamp}/run.log` |
|
|
|
var runaslog = JSON.parse('[' + fs.readFileSync(runlogjson, { flags: 'a+' }) + ']'); |
|
|
|
try { fs.unlinkSync(runlogjson) } catch(e){ } // PB : TODO -- Have a unique file for each elevated run. |
|
|
|
// console.log( "runaslog : " + runaslog.length ) |
|
|
|
// Assemble elevated run results into the main run log |
|
|
|
runaslog.forEach((logEntry) => { |
|
|
|
this.statuslog.statuslog(logEntry.success ? null : logEntry, logEntry) |
|
|
|
logEntry.success ? (console.log(['success :' + (logEntry.result || logEntry.success)]), console.log((logEntry.messages || []).join(' '))) : (console.error(['error :' + logEntry.result]), console.error((logEntry.messages || []).join(' '))) |
|
|
|
}) |
|
|
|
} |
|
|
|
catch (e) { |
|
|
|
// We must have a runas log |
|
|
|
this.statuslog.statuslog(e) |
|
|
|
console.error('Run log error probably was not created by runas : ' + e) |
|
|
|
} |
|
|
|
}) |
|
|
|
.catch(err => console.error('Elevation failed : ' + err)); |
|
|
|
}) |
|
|
|
} |
|
|
|
, ensureDirectoryExistence(filePath) { |
|
|
|
var dirname = path.dirname(filePath); |
|
|
|
if (fs.existsSync(dirname)) { |
|
|
|
return filePath; |
|
|
|
} |
|
|
|
this.ensureDirectoryExistence(dirname); |
|
|
|
fs.mkdirSync(dirname); |
|
|
|
return filePath; |
|
|
|
} |
|
|
|
|
|
|
|
, generateDependencies(){ |
|
|
|
// PB : TODO -- Keep only the last n runs... |
|
|
|
// Currently it retains 2*n when proc needs to be relaunched in elevated mode !!! |
|
|
|
|
|
|
|
this.ensureDirectoryExistence(`${this.selectedinstance.root}/.elxr/run-${this.runtimestamp}/download.bat`) |
|
|
|
fs.writeFileSync(this.ensureDirectoryExistence(path.normalize(`${this.selectedinstance.root}/${this.downloadsdir}/readme.txt`)), `${this.getVersion()} Your local downloads for this instance`) |
|
|
|
|
|
|
|
// PB : TODO include and build from files... using rollup.. |
|
|
|
var downloadbatch = |
|
|
|
`::************************************************************************** |
|
|
|
:Download_ <url> <File> |
|
|
|
Powershell.exe ^ |
|
|
|
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'; ^ |
|
|
|
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols; ^ |
|
|
|
(New-Object System.Net.WebClient).DownloadFile('%1','%2') |
|
|
|
exit /b |
|
|
|
::**************************************************************************` |
|
|
|
fs.writeFileSync(`${this.selectedinstance.root}/.elxr/run-${this.runtimestamp}/download.bat`, downloadbatch) |
|
|
|
|
|
|
|
|
|
|
|
var windowselevate = |
|
|
|
` |
|
|
|
<html><HTA:APPLICATION ID="windowselevate" icon="#"/> |
|
|
|
<script language="vbscript"> |
|
|
|
document.title = "elxr control panel" |
|
|
|
self.ResizeTo 1024,1000 |
|
|
|
|
|
|
|
Sub Window_Onload |
|
|
|
self.MoveTo (screen.availWidth - (screen.availWidth/2 + 40)),10 |
|
|
|
End Sub |
|
|
|
|
|
|
|
Set objShell = CreateObject("WScript.Shell") |
|
|
|
Set objENV = objShell.Environment("Process") |
|
|
|
dim NODE_ENV |
|
|
|
NODE_ENV = objENV("NODE_ENV") |
|
|
|
|
|
|
|
</script> |
|
|
|
|
|
|
|
<script language="javascript"> |
|
|
|
//WINDOWSTATE="minimize" SHOWINTASKBAR="no" SYSMENU="no" CAPTION="no" |
|
|
|
// https://devblogs.microsoft.com/scripting/how-can-i-pass-command-line-variables-to-an-hta-when-it-starts/ |
|
|
|
// alert(windowselevate.commandLine) |
|
|
|
var args = windowselevate.commandLine.split('"').slice(3); |
|
|
|
// alert(args) |
|
|
|
var processedArgs = { _ : [] } |
|
|
|
var namedArgs = []; |
|
|
|
namedArgs.push('--wd=' + objENV('wd')) |
|
|
|
// alert(namedArgs) |
|
|
|
for(var item in args){ |
|
|
|
if(args[item].charAt(0) === '-'){ |
|
|
|
namedArgs.push(args[item]) |
|
|
|
var split = args[item].split('='); |
|
|
|
processedArgs[split[0].slice(2)] = split[1] || true; |
|
|
|
} |
|
|
|
else processedArgs._.push(args[item]); |
|
|
|
} |
|
|
|
var awaitrun = true; |
|
|
|
if(!processedArgs.awaitrun) { |
|
|
|
awaitrun = false; |
|
|
|
delete processedArgs.awaitrun |
|
|
|
} |
|
|
|
|
|
|
|
// args = args.forEach(function(item){ }) |
|
|
|
// alert('processedArgs._ : ' + processedArgs._); |
|
|
|
// alert(processedArgs.runas); |
|
|
|
// alert(objENV('wd')) |
|
|
|
// PB : TODO -- Convert all the cli args back to string. |
|
|
|
// __filename will sure we are launhed using the same entry point. |
|
|
|
var cargs = (processedArgs.debug ? '--inspect-brk=9226' : '') + ' ${__filename.replace(/\\/g, '\\\\').replace("win_verse", "index")} ' + processedArgs._.join(' ') + ' ' + namedArgs.join(' '); |
|
|
|
var shell = new ActiveXObject('shell.application'); |
|
|
|
// alert('launching node privilged. ' + processedArgs['nodepath']) |
|
|
|
// shell.ShellExecute('cmd.exe', '/k where node', '', '', 10); |
|
|
|
// shell.ShellExecute('cmd.exe', '/k notepad.exe', '', 'runas', 1); |
|
|
|
// shell.ShellExecute('cmd.exe ', '/k node ', '', 'runas', 1); |
|
|
|
// shell.ShellExecute('cmd.exe ', '/k node ' + cargs + '', '', 'runas', 1); |
|
|
|
// alert(cargs) |
|
|
|
function run(runas){ shell.ShellExecute('node', cargs, '', runas, 1); } |
|
|
|
if(!awaitrun) { run('runas') } |
|
|
|
|
|
|
|
var log = document.createElement('div'); |
|
|
|
log.innerHTML='Please Wait'; |
|
|
|
function l(msg){ log.innerHTML+= msg; }; |
|
|
|
// log.style.color = 'blue'; |
|
|
|
log.style.width = '95%'; |
|
|
|
log.id = 'log'; |
|
|
|
|
|
|
|
function endconsole(from){ |
|
|
|
// alert('endconsole ' + from) |
|
|
|
if(fso.FileExists("run.done")) { |
|
|
|
fso.DeleteFile('run.done') // PB : TODO -- IPC through files is needed only for windows we need to do it per run... |
|
|
|
} |
|
|
|
// alert('closing window') |
|
|
|
window.close(); |
|
|
|
}; |
|
|
|
var timer = function(){ |
|
|
|
// alert('here') |
|
|
|
l('.'); |
|
|
|
if(fso.FileExists("run.done")) { |
|
|
|
endconsole('timer') |
|
|
|
} |
|
|
|
else window.setTimeout(timer, 1000); |
|
|
|
}; |
|
|
|
|
|
|
|
// alert('/k node ' + cargs + '') |
|
|
|
// shell.ShellExecute(processedArgs['nodepath'], cargs, '', 'runas', 1); |
|
|
|
var fso = new ActiveXObject('Scripting.FileSystemObject'); |
|
|
|
|
|
|
|
function i(html){ var e = document.createElement('div'); e.innerHTML = html; document.body.appendChild(e); }; |
|
|
|
|
|
|
|
// window.onbeforeunload = function (e) { |
|
|
|
// endconsole('onbeforeunload') |
|
|
|
// }; |
|
|
|
window.onload = function() { |
|
|
|
document.body.style.backgroundColor = 'black'; |
|
|
|
document.body.style.fontFamily = 'arial'; |
|
|
|
document.body.style.color = 'cyan'; |
|
|
|
var cmds = document.createElement('div'); |
|
|
|
cmds.innerHTML = ''; |
|
|
|
cmds.w = function(msg){ cmds.innerHTML+= msg; }; |
|
|
|
cmds.w('<Br/>Current config : ') |
|
|
|
// cmds.w('<div style="width:50%" onclick="endconsole()">X</div>' ) |
|
|
|
cmds.w('<input type="button" value="Close Console" onclick="endconsole()">') |
|
|
|
cmds.w('<Br/>NODE_ENV = ' + NODE_ENV) |
|
|
|
var namedArgs = []; |
|
|
|
for(var v in processedArgs){ |
|
|
|
if(v != '_' && v!== 'runtimestamp') { |
|
|
|
namedArgs.push('--' + v + '=' + processedArgs[v]) |
|
|
|
} |
|
|
|
} |
|
|
|
// cmds.w('<Br/>cmd = ' + processedArgs._.join(' ') + ' ' + namedArgs.join(' ') ) |
|
|
|
cmds.w('<Br/><Br/>') |
|
|
|
cmds.w('cmd = <input style="width:80%" type="text" value="' + processedArgs._.join(' ') + ' ' + namedArgs.join(' ') + '"></input> <input type="button" value="Run" onclick="run()"></input>') |
|
|
|
cmds.w('${toHTML('<option>', Object.keys(this.cmds), '</option>', '<select>', '</select>')}'); |
|
|
|
|
|
|
|
// cmds.style.color = 'blue'; |
|
|
|
// processedArgs._[1] === 'use' ? l('<Br/>using = ' + processedArgs._[2]) : null; |
|
|
|
|
|
|
|
|
|
|
|
document.body.appendChild(cmds); |
|
|
|
document.body.appendChild(log); |
|
|
|
|
|
|
|
// alert(fso.GetAbsolutePathName(".")) |
|
|
|
window.setTimeout(timer, 3000); |
|
|
|
}; |
|
|
|
|
|
|
|
</script> |
|
|
|
</html> |
|
|
|
|
|
|
|
` |
|
|
|
fs.writeFileSync(`${this.selectedinstance.root}/.elxr/run-${this.runtimestamp}/windowselevate.hta`, windowselevate) |
|
|
|
} |
|
|
|
, getchrome(){ |
|
|
|
// Chrome install location from registry. |
|
|
|
return shell_verse.regread("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe\\").then(chromepath => { |
|
|
|
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe |
|
|
|
console.log(chromepath) |
|
|
|
return chromepath |
|
|
|
}); |
|
|
|
} |
|
|
|
, regread(s){ |
|
|
|
var uniquerun = (new Date()).getTime() |
|
|
|
var out = `${this.selectedinstance.root}/.elxr/run-${this.runtimestamp}/${uniquerun}-regread.out` |
|
|
|
var outescaped = out.replace(/\\/g,"\\\\") |
|
|
|
// console.log('out ::: ' + out) |
|
|
|
fs.writeFileSync(`${this.selectedinstance.root}/.elxr/run-${this.runtimestamp}/${uniquerun}-regread.js`, |
|
|
|
` |
|
|
|
// WScript.Echo('------------UNNAMED-----------') |
|
|
|
// WScript.Echo(WScript.Arguments.Item(0)) |
|
|
|
argEnumerator = new Enumerator(WScript.Arguments.Unnamed) |
|
|
|
var args = [] |
|
|
|
for(; !argEnumerator.atEnd(); argEnumerator.moveNext()){ |
|
|
|
args.push('' + argEnumerator.item() + '') |
|
|
|
// WScript.Echo('-arg-' + argEnumerator.item() + '-arg-') // Value |
|
|
|
} |
|
|
|
|
|
|
|
// WScript.Echo(args[0]) |
|
|
|
var objShell = WScript.createobject("wscript.shell") |
|
|
|
if(!args[0]) { |
|
|
|
WScript.Echo( '' ) |
|
|
|
} |
|
|
|
else { |
|
|
|
WScript.Echo(args[0]) |
|
|
|
var read = objShell.RegRead(args[0]) |
|
|
|
try { |
|
|
|
var fs = new ActiveXObject('Scripting.FileSystemObject'); |
|
|
|
var fh = fs.CreateTextFile('${outescaped}', true); |
|
|
|
fh.WriteLine(read); |
|
|
|
fh.Close(); |
|
|
|
} |
|
|
|
catch(e){ WScript.Echo(e.message) } |
|
|
|
// WScript.Echo( read ) |
|
|
|
} |
|
|
|
|
|
|
|
` |
|
|
|
) |
|
|
|
return nodeShellExec('cscript', [path.normalize(`${this.selectedinstance.root}/.elxr/run-${this.runtimestamp}/${uniquerun}-regread.js`), `${s}`]) |
|
|
|
.then(read => { |
|
|
|
// console.log("REGREAD RESULT : " + read) |
|
|
|
// console.dir(read) |
|
|
|
if(read.success) { |
|
|
|
return (fs.readFileSync(out) + "").trim() |
|
|
|
} |
|
|
|
else throw 'regread failed' |
|
|
|
}) |
|
|
|
} |
|
|
|
, iswin(){ return true} |
|
|
|
, islin(){ return false} |
|
|
|
} |