Skip to content

Instantly share code, notes, and snippets.

@vweevers
Last active August 29, 2015 14:01
Show Gist options
  • Save vweevers/e8c3863759fd8f9659ce to your computer and use it in GitHub Desktop.
Save vweevers/e8c3863759fd8f9659ce to your computer and use it in GitHub Desktop.
Test script to fix Windows browser detection (https://github.com/substack/browser-launcher/issues/7)
var util = require('util')
, exec = require('child_process').exec
, execOptions = { timeout: 3000 }
, wmicCommand = 'wmic datafile where Name="%s" get Version /format:value'
, rootKeys = ['HKEY_LOCAL_MACHINE', 'HKEY_CURRENT_USER']
// Convenient access to environment variables
var env = process.env
, pf = env['ProgramFiles(x86)'] || env['ProgramFiles']
, pf64 = env.ProgramW6432 // "C:\Program Files" on x64
, home = env.USERPROFILE || (env.HOMEDRIVE + env.HOMEPATH)
// "%USERPROFILE%\AppData\Local" or
// "%USERPROFILE%\Local Settings\Application Data" (XP)
, local = env.LOCALAPPDATA || (home + '\\Local Settings\\Application Data')
// Default locations and registry keys
var browsers = {
chrome: {
registry: [ // Expanded to HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER
'"%s\\Software\\Google\\Update" /v LastInstallerSuccessLaunchCmdLine',
'"%s\\Software\\Wow6432Node\\Google\\Update" /v LastInstallerSuccessLaunchCmdLine',
'"%s\\Software\\Clients\\StartMenuInternet\\Google Chrome\\shell\\open\\command"',
'"%s\\SOFTWARE\\Wow6432Node\\Clients\\StartMenuInternet\\Google Chrome\\shell\\open\\command"',
'"%s\\SOFTWARE\\Classes\\ChromeHTML\\shell\\open\\command"',
// Parsing this seems to fail, needs testing
'"%s\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe"'
],
locations: [ // Joined with filename (or [browsername].exe)
[pf, 'Google\\Chrome\\Application'],
[local, 'Google\\Chrome\\Application']
]
},
ff: {
registry: [
'"%s\\Software\\Clients\\StartMenuInternet\\FIREFOX.EXE\\shell\\open\\command"',
'"%s\\Software\\Mozilla\\Mozilla Firefox\\" /s /v PathToExe',
// First get version, then path
[ '"%s\\Software\\Mozilla\\Mozilla Firefox" /v CurrentVersion',
'"%s\\Software\\Mozilla\\Mozilla Firefox\\%s\\Main\\" /s /v PathToExe' ]
],
file: 'firefox.exe',
locations: [
[pf, 'Mozilla Firefox']
]
},
ie: {
registry: [
'"%s\\Software\\Clients\\StartMenuInternet\\IEXPLORE.EXE\\shell\\open\\command"'
],
file: 'iexplore.exe',
locations: [
[pf64, 'Internet Explorer'],
[pf, 'Internet Explorer']
]
},
// Untested and incomplete (Safari for Windows is dead anyway)
safari: {
registry: [
'"%s\\Software\\Apple Computer, Inc.\\Safari" /v BrowserExe'
]
}
}
// TODO: get all paths, merge, then query WMIC
Object.keys(browsers).forEach(function(browser){
var entry = browsers[browser]
, file = entry.file || (browser + '.exe')
// 1st strategy: registry
if (entry.registry) {
entry.registry.forEach(function(query){
var withVersion = Array.isArray(query)
rootKeys.forEach(function(root){
if (withVersion) {
var cmd = 'reg query ' + util.format(query[0], root)
exec(cmd, parseVersionQuery.bind(null, query[1]))
return
}
var cmd = 'reg query ' + util.format(query, root)
exec(cmd, parsePathQuery())
})
})
}
// 2nd strategy: common locations
if (entry.locations)
iterate.call({
browser: browser,
paths: entry.locations.filter(notEmpty).map(join),
strategy: 'common location'
})
// 3rd strategy: find in PATH
// - "where" utility is available on > XP
exec('where '+file, parseWhere, execOptions)
// Functions (todo: without closures)
function parseVersionQuery(pathQuery, err, stdout) {
if (err) return
var matches = /([\d\.]+)/.exec(stdout.toString())
if (!matches || !matches[1]) {
return console.log("can't parse reg version", stdout)
}
var version = matches[1]
rootKeys.forEach(function(root){
var cmd = 'reg query ' + util.format(pathQuery, root, version)
exec(cmd, parsePathQuery(function(path){
found(browser, version, path, 'registry with version')
}))
})
}
function parsePathQuery(cb) {
return function(err, stdout) {
if (err) return
var matches = /([A-Z]:\\[^"$]+)/i.exec(stdout.toString())
if (!matches || !matches[1]) {
return console.log("can't parse reg path", stdout)
}
var path = matches[1].trim();
if (cb) return cb(path);
getVersion(path, function(version){
if (version) found(
browser, version, path, 'registry'
)
})
}
}
function parseWhere(err, out){
if (err) return
out = out.toString()
.trim()
.split(/\s*\n\s*/)
.filter(drive)
if (out.length) iterate.call({
browser: browser,
paths: out,
strategy: 'in PATH'
})
}
function join(path) {
path.push(file)
return path.join('\\')
}
})
function notEmpty(path) {
return path && path[0]
}
function drive(path) {
return path[1] === ':' && path[2] === '\\'
}
function iterate(version) {
if (!this.i) this.i = 0
if (version)
found(
this.browser, version,
this.path, this.strategy
)
if (this.path = this.paths[this.i++])
getVersion(this.path, iterate.bind(this))
}
function found(browser, version, path, strategy) {
console.log(util.format(
"\n%s %s [%s]\n @ %s",
browser, version, strategy, path
))
}
function getVersion(path, cb) {
// WMIC wants double slashes
var escaped = path.replace(/\\/g, '\\\\')
, cmd = util.format(wmicCommand, escaped)
exec(cmd, parse, execOptions)
function parse (err, stdout, stderr){
if (err) return cb()
// "Version=xx"
var a = stdout.toString().trim().split('=')
if (a.length===2) cb(a[1])
else cb()
}
}
chrome 35.0.1916.114 [common location]
@ C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
ff 23.0.1.4974 [common location]
@ C:\Program Files (x86)\Mozilla Firefox\firefox.exe
ie 11.0.9600.17041 [common location]
@ C:\Program Files\Internet Explorer\iexplore.exe
ie 11.0.9600.17041 [registry]
@ C:\Program Files\Internet Explorer\iexplore.exe
chrome 35.0.1916.114 [registry]
@ C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
chrome 35.0.1916.114 [registry]
@ C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
chrome 35.0.1916.114 [registry]
@ C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
ff 23.0.1.4974 [registry]
@ C:\Program Files (x86)\Mozilla Firefox\firefox.exe
ie 11.0.9600.17041 [common location]
@ C:\Program Files (x86)\Internet Explorer\iexplore.exe
@vweevers
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment