2017-08-22 01:11:07 +00:00
|
|
|
'use strict'
|
|
|
|
|
|
|
|
const networkPattern = /network[\s\S]*?=[\s\S]*?{([\s\S]*?)}/gi
|
|
|
|
const quoteRe = new RegExp('"', 'g')
|
|
|
|
|
|
|
|
const filePath = '/etc/wpa_supplicant/wpa_supplicant.conf'
|
|
|
|
const reconfigure = '/sbin/wpa_cli reconfigure'
|
|
|
|
const refresh = '/sbin/ifdown wlan0 && /sbin/ifup --force wlan0'
|
|
|
|
const iwlist = '/sbin/iwlist wlan0 scanning | grep "ESSID:"'
|
|
|
|
const iwgetid = '/sbin/iwgetid'
|
|
|
|
|
2017-09-25 02:27:53 +00:00
|
|
|
const log = require('../log')('wifi')
|
2017-08-22 01:11:07 +00:00
|
|
|
const exec = require('child_process').exec
|
|
|
|
const fs = require('fs')
|
|
|
|
|
2017-10-21 16:10:16 +00:00
|
|
|
let _entry = null
|
|
|
|
let _ssid = null
|
|
|
|
let _cb = null
|
|
|
|
|
2017-09-17 19:50:14 +00:00
|
|
|
/** Class representing the wifi features */
|
|
|
|
class Wifi {
|
2017-08-22 01:11:07 +00:00
|
|
|
constructor () {
|
2017-10-21 16:10:16 +00:00
|
|
|
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
2017-09-17 19:50:14 +00:00
|
|
|
/**
|
|
|
|
* List available wifi access points
|
|
|
|
*
|
|
|
|
* @param {function} callback Function which gets invoked after list is returned
|
|
|
|
*/
|
2017-08-22 01:11:07 +00:00
|
|
|
list (callback) {
|
|
|
|
exec(iwlist, (err, stdout, stderr) => {
|
|
|
|
if (err) {
|
|
|
|
console.error(err)
|
|
|
|
return callback(err)
|
|
|
|
}
|
2019-03-26 20:08:16 +00:00
|
|
|
const limit = 20;
|
2017-08-22 01:11:07 +00:00
|
|
|
const lines = stdout.split('\n')
|
2018-01-01 03:27:02 +00:00
|
|
|
let output = []
|
2017-08-22 01:11:07 +00:00
|
|
|
let line
|
2018-01-09 18:39:43 +00:00
|
|
|
let i = 0
|
2017-08-22 01:11:07 +00:00
|
|
|
for (let l of lines) {
|
|
|
|
line = l.replace('ESSID:', '').trim()
|
2019-03-26 20:08:16 +00:00
|
|
|
if (line !== '""' && i < limit) {
|
2017-08-22 01:11:07 +00:00
|
|
|
line = line.replace(quoteRe, '')
|
|
|
|
output.push(line)
|
|
|
|
}
|
2018-01-09 18:39:43 +00:00
|
|
|
i++
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
2018-01-01 03:03:42 +00:00
|
|
|
output = output.filter(ap => {
|
|
|
|
if (ap !== '') return ap
|
|
|
|
})
|
2017-08-22 01:11:07 +00:00
|
|
|
return callback(null, output)
|
|
|
|
})
|
|
|
|
}
|
2017-09-17 19:50:14 +00:00
|
|
|
/**
|
|
|
|
* (internal function) Invoked after config file is read,
|
|
|
|
* then invokes file write on the config file
|
|
|
|
*
|
|
|
|
* @param {object} err (optional) Error object only present if problem reading config file
|
|
|
|
* @param {string} data Contents of the config file
|
|
|
|
*/
|
2017-08-22 01:11:07 +00:00
|
|
|
_readConfigCb (err, data) {
|
2018-01-01 04:45:40 +00:00
|
|
|
let parsed
|
2018-01-01 05:04:49 +00:00
|
|
|
let current
|
2017-08-22 01:11:07 +00:00
|
|
|
if (err) {
|
|
|
|
console.error(err)
|
2017-10-21 16:10:16 +00:00
|
|
|
return _cb(err)
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
2018-01-01 05:38:08 +00:00
|
|
|
parsed = this._parseConfig(data)
|
2018-01-01 05:10:50 +00:00
|
|
|
current = parsed.find(network => {
|
|
|
|
return network.ssid === _ssid
|
2018-01-01 05:04:49 +00:00
|
|
|
})
|
|
|
|
if (typeof current !== 'undefined') {
|
|
|
|
data = data.replace(current.raw, _entry)
|
2017-08-22 01:11:07 +00:00
|
|
|
} else {
|
2018-01-01 05:04:49 +00:00
|
|
|
data += '\n\n' + _entry
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
2017-10-21 16:10:16 +00:00
|
|
|
_entry = null
|
2018-01-01 05:34:24 +00:00
|
|
|
fs.writeFile(filePath, data, 'utf8', this._writeConfigCb.bind(this))
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
2017-09-17 19:50:14 +00:00
|
|
|
/**
|
|
|
|
* (internal function) Invoked after config file is written,
|
|
|
|
* then executes reconfiguration command
|
|
|
|
*
|
|
|
|
* @param {object} err (optional) Error object only present if problem writing config file
|
|
|
|
*/
|
2017-08-22 01:11:07 +00:00
|
|
|
_writeConfigCb (err) {
|
|
|
|
if (err) {
|
|
|
|
console.error(err)
|
2017-10-21 16:10:16 +00:00
|
|
|
return _cb(err)
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
2018-01-01 05:34:24 +00:00
|
|
|
exec(reconfigure, this._reconfigureCb.bind(this))
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
2017-09-17 19:50:14 +00:00
|
|
|
/**
|
|
|
|
* (internal function) Invoked after reconfiguration command is complete
|
|
|
|
*
|
|
|
|
* @param {object} err (optional) Error object only present if configuration command fails
|
|
|
|
* @param {string} stdout Standard output from reconfiguration command
|
|
|
|
* @param {string} stderr Error output from command if fails
|
|
|
|
*/
|
2017-08-22 01:11:07 +00:00
|
|
|
_reconfigureCb (err, stdout, stderr) {
|
|
|
|
if (err) {
|
|
|
|
console.error(err)
|
2017-10-21 16:10:16 +00:00
|
|
|
return _cb(err)
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
|
|
|
console.log('Wifi reconfigured')
|
2018-01-01 05:34:24 +00:00
|
|
|
exec(refresh, this._refreshCb.bind(this))
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
2017-09-17 19:50:14 +00:00
|
|
|
/**
|
|
|
|
* (internal function) Invoked after wifi refresh command is complete
|
|
|
|
*
|
|
|
|
* @param {object} err (optional) Error object only present if refresh command fails
|
|
|
|
* @param {string} stdout Standard output from refresh command
|
|
|
|
* @param {string} stderr Error output from command if fails
|
|
|
|
*/
|
2017-08-22 01:11:07 +00:00
|
|
|
_refreshCb (err, stdout, stderr) {
|
|
|
|
if (err) {
|
|
|
|
console.error(err)
|
2017-10-21 16:10:16 +00:00
|
|
|
return _cb(err)
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
|
|
|
console.log('Wifi refreshed')
|
2018-01-01 05:37:00 +00:00
|
|
|
_cb(null, { ssid : _ssid })
|
2017-10-21 16:10:16 +00:00
|
|
|
_cb = () => {}
|
2018-01-01 04:16:01 +00:00
|
|
|
}
|
2018-01-01 05:38:08 +00:00
|
|
|
_parseConfig (str) {
|
|
|
|
const networks = []
|
|
|
|
const lines = str.split('\n')
|
|
|
|
let network = {}
|
|
|
|
for (let line of lines) {
|
|
|
|
if (line.substring(0, 9) === 'network={') {
|
|
|
|
network = {}
|
|
|
|
network.raw = line
|
|
|
|
} else if (network.raw && line.indexOf('ssid=') !== -1) {
|
|
|
|
network.ssid = line.replace('ssid=', '').trim().replace(quoteRe, '')
|
|
|
|
if (network.raw) {
|
|
|
|
network.raw += '\n' + line
|
|
|
|
}
|
|
|
|
} else if (network.raw && line.substring(0, 1) === '}') {
|
|
|
|
network.raw += '\n' + line
|
|
|
|
networks.push(network)
|
|
|
|
network = {}
|
|
|
|
} else if (network.raw) {
|
|
|
|
network.raw += '\n' + line
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return networks
|
|
|
|
}
|
2018-01-01 01:55:54 +00:00
|
|
|
/**
|
2018-01-01 02:32:09 +00:00
|
|
|
* Create sanitized wpa_supplicant.conf stanza for
|
2018-01-01 01:55:54 +00:00
|
|
|
* configuring wifi without storing plaintext passwords
|
|
|
|
* @example
|
|
|
|
* network={
|
|
|
|
* ssid="YOUR_SSID"
|
|
|
|
* #psk="YOUR_PASSWORD"
|
|
|
|
* psk=6a24edf1592aec4465271b7dcd204601b6e78df3186ce1a62a31f40ae9630702
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* @param {string} ssid SSID of wifi network
|
2018-01-01 02:32:09 +00:00
|
|
|
* @param {string} pwd Plaintext passphrase of wifi network
|
|
|
|
* @param {function} callback Function called after psk hash is generated
|
2018-01-01 01:55:54 +00:00
|
|
|
*/
|
2018-01-01 02:32:09 +00:00
|
|
|
createPSK (ssid, pwd, callback) {
|
2018-01-01 01:55:54 +00:00
|
|
|
const cmd = `wpa_passphrase "${ssid}" "${pwd}" | grep "psk="`
|
2018-01-01 02:06:42 +00:00
|
|
|
let lines
|
|
|
|
let hash
|
|
|
|
let plaintext
|
2018-01-01 01:55:54 +00:00
|
|
|
exec(cmd, (err, stdout, stderr) => {
|
|
|
|
if (err) {
|
|
|
|
return callback(err)
|
|
|
|
}
|
2018-01-01 02:06:42 +00:00
|
|
|
lines = stdout.replace('#psk=', '').split('psk=')
|
|
|
|
hash = lines[1]
|
|
|
|
plaintext = lines[0]
|
|
|
|
callback(null, hash.trim(), plaintext.trim())
|
2018-01-01 01:55:54 +00:00
|
|
|
})
|
|
|
|
}
|
2017-09-17 19:50:14 +00:00
|
|
|
/**
|
|
|
|
* Function which initializes the processes for adding a wifi access point authentication
|
|
|
|
*
|
|
|
|
* @param {string} ssid SSID of network to configure
|
2018-01-01 02:23:31 +00:00
|
|
|
* @param {string} pwd Password of access point, plaintext to be masked
|
2018-01-01 02:06:42 +00:00
|
|
|
* @param {string} hash Password/SSID of access point, securely hashed
|
2017-09-17 19:50:14 +00:00
|
|
|
* @param {function} callback Function invoked after process is complete, or fails
|
|
|
|
*/
|
2018-01-01 02:06:42 +00:00
|
|
|
setNetwork (ssid, pwd, hash, callback) {
|
2018-01-01 02:23:31 +00:00
|
|
|
let masked = pwd.split('').map(char => { return char !== '"' ? '*' : '"' }).join('')
|
2018-01-01 02:06:42 +00:00
|
|
|
_entry = `network={\n\tssid="${ssid}"\n\t#psk=${masked}\n\tpsk=${hash}\n}\n`
|
2017-10-21 16:10:16 +00:00
|
|
|
_cb = callback
|
|
|
|
_ssid = ssid
|
2018-01-01 05:34:24 +00:00
|
|
|
fs.readFile(filePath, 'utf8', this._readConfigCb.bind(this))
|
2017-08-22 01:11:07 +00:00
|
|
|
}
|
2017-09-17 19:50:14 +00:00
|
|
|
/**
|
|
|
|
* Executes command which gets the currently connected network
|
|
|
|
*
|
|
|
|
* @param {function} callback Function which is invoked after command is completed
|
|
|
|
*/
|
2017-08-22 01:11:07 +00:00
|
|
|
getNetwork (callback) {
|
2018-01-01 02:23:31 +00:00
|
|
|
let output
|
2017-08-22 01:11:07 +00:00
|
|
|
exec(iwgetid, (err, stdout, stderr) => {
|
|
|
|
if (err) {
|
|
|
|
return callback(err)
|
|
|
|
}
|
2018-01-01 02:23:31 +00:00
|
|
|
output = stdout.split('ESSID:')[1].replace(quoteRe, '').trim()
|
|
|
|
callback(null, output)
|
2017-08-22 01:11:07 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-17 19:50:14 +00:00
|
|
|
module.exports = new Wifi()
|