From 98281bab489f97cb2e466fd27963f312e5e0d543 Mon Sep 17 00:00:00 2001 From: Sebastian Hildebrandt Date: Sun, 5 Dec 2021 19:15:33 +0100 Subject: [PATCH] wifiNetworks() adaption for Apple silicon (mac OS) --- CHANGELOG.md | 1 + docs/history.html | 5 ++ docs/index.html | 2 +- lib/util.js | 153 ++++++++++++++++++++++++---------------------- lib/wifi.js | 112 +++++++++++++++++++++------------ 5 files changed, 159 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ba1540..6e3e780 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ For major (breaking) changes - **version 4, 3 and 2** - see end of page. | Version | Date | Comment | | -------------- | -------------- | -------- | +| 5.9.16 | 2021-12-05 | `wifiNetworks()` adaption for Apple silicon (mac OS) | | 5.9.15 | 2021-11-19 | `cpuCache()` fix (windows) | | 5.9.14 | 2021-11-17 | `versions()` python 2 monterey (deprecated warning) fix (mac OS) | | 5.9.13 | 2021-11-14 | `time()` timezone name, `l1 cache` improvements | diff --git a/docs/history.html b/docs/history.html index 29b529c..e99a8cc 100644 --- a/docs/history.html +++ b/docs/history.html @@ -57,6 +57,11 @@ + + 5.9.16 + 2021-12-05 + wifiNetworks() adaption for Apple silicon (mac OS) + 5.9.15 2021-11-19 diff --git a/docs/index.html b/docs/index.html index a72af80..849d2e2 100644 --- a/docs/index.html +++ b/docs/index.html @@ -170,7 +170,7 @@
systeminformation
 
-
New Version: 5.9.15
+
New Version: 5.9.16
diff --git a/lib/util.js b/lib/util.js index c2dbd0d..c623c33 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1111,91 +1111,98 @@ function linuxVersion() { } function plistParser(xmlStr) { - const tags = ['array', 'dict', 'key', 'string', 'integer', 'date', 'real', 'data']; + const tags = ['array', 'dict', 'key', 'string', 'integer', 'date', 'real', 'data', 'boolean', 'arrayEmpty']; + const startStr = ' { - const ii = xmlStr.indexOf(`<${tag}>`); - if (ii !== -1 && ii < result.pos) { - result = { - pos: ii, - tag - }; - } - }); - return result; + let pos = xmlStr.indexOf(startStr); + let len = xmlStr.length; + while (xmlStr[pos] !== '>' && pos < len) { + pos++; } - function getNextClosingTagPos(tag) { - return xmlStr.indexOf(``); - } + let depth = 0; + let inTagStart = false; + let inTagContent = false; + let inTagEnd = false; + let metaData = [{ tagStart: '', tagEnd: '', tagContent: '', key: '', data: null }]; + let c = ''; + let cn = xmlStr[pos]; - function parseXmlTree(isArray, closingTag) { - // start parsing - let obj = {}; - let arr = []; - let cpos = getNextTagPos(); - let key = ''; - let valueStr = null; - - while (cpos.pos >= 0 && cpos.tag) { - let nextTagPos = getNextTagPos(); - // let nextClosePos = getNextClosingTagPos(cpos.tag); - let nextClosePosBlock = closingTag ? getNextClosingTagPos(closingTag) : 999999; - if (nextClosePosBlock <= nextTagPos.pos) { - return (isArray ? arr : obj); + while (pos < len) { + c = cn; + if (pos + 1 < len) { cn = xmlStr[pos + 1]; } + if (c === '<') { + inTagContent = false; + if (cn === '/') { inTagEnd = true; } + else if (metaData[depth].tagStart) { + metaData[depth].tagContent = ''; + if (!metaData[depth].data) { metaData[depth].data = metaData[depth].tagStart === 'array' ? [] : {}; } + depth++; + metaData.push({ tagStart: '', tagEnd: '', tagContent: '', key: null, data: null }); + inTagStart = true; + inTagContent = false; } - xmlStr = xmlStr.substring((cpos.pos + cpos.tag.length + 2)); - if (cpos.tag === 'array') { - const res = parseXmlTree(true, cpos.tag); - if (key) { - obj[key] = res; - } else { - obj = res; + else if (!inTagStart) { inTagStart = true; } + } else if (c === '>') { + if (metaData[depth].tagStart === 'true/') { inTagStart = false; inTagEnd = true; metaData[depth].tagStart = ''; metaData[depth].tagEnd = '/boolean'; metaData[depth].data = true; } + if (metaData[depth].tagStart === 'false/') { inTagStart = false; inTagEnd = true; metaData[depth].tagStart = ''; metaData[depth].tagEnd = '/boolean'; metaData[depth].data = false; } + if (metaData[depth].tagStart === 'array/') { inTagStart = false; inTagEnd = true; metaData[depth].tagStart = ''; metaData[depth].tagEnd = '/arrayEmpty'; metaData[depth].data = []; } + if (inTagContent) { inTagContent = false; } + if (inTagStart) { + inTagStart = false; + inTagContent = true; + if (metaData[depth].tagStart === 'array') { + metaData[depth].data = []; } - } else if (cpos.tag === 'dict') { - const res = parseXmlTree(false, cpos.tag); - if (!isArray) { - obj[key] = res; - } else { - arr.push(res); + if (metaData[depth].tagStart === 'dict') { + metaData[depth].data = {}; } - xmlStr = xmlStr.substring((cpos.pos + cpos.tag.length + 3)); - } else { - let nextTagPos = getNextTagPos(); - let nextClosePos = getNextClosingTagPos(cpos.tag); - // nextClosePosBlock = closingTag ? getNextClosingTagPos(closingTag) : 999999; - if (nextClosePos < nextTagPos.pos) { - if (cpos.tag === 'key') { - key = xmlStr.substring(0, nextClosePos); - xmlStr = xmlStr.substring(key.length + cpos.tag.length + 3); // key done - } else { - valueStr = xmlStr.substring(0, nextClosePos).replace(/\t/g, ''); - if (cpos.tag === 'string') { if (!isArray) { obj[key] = valueStr; } else { arr.push(valueStr); } } - if (cpos.tag === 'integer') { if (!isArray) { obj[key] = parseInt(valueStr); } else { arr.push(parseInt(valueStr)); } } - if (cpos.tag === 'date') { if (!isArray) { obj[key] = valueStr; } else { arr.push(valueStr); } } - if (cpos.tag === 'data') { if (!isArray) { obj[key] = valueStr; } else { arr.push(valueStr); } } - if (cpos.tag === 'real') { if (!isArray) { obj[key] = parseFloat(valueStr); } else { arr.push(parseFloat(valueStr)); } } - - key = ''; - xmlStr = xmlStr.substring(valueStr.length + cpos.tag.length + 3); // property done + } + if (inTagEnd) { + inTagEnd = false; + if (metaData[depth].tagEnd && tags.indexOf(metaData[depth].tagEnd.substr(1)) >= 0) { + if (metaData[depth].tagEnd === '/dict' || metaData[depth].tagEnd === '/array') { + if (depth > 1 && metaData[depth - 2].tagStart === 'array') { + metaData[depth - 2].data.push(metaData[depth - 1].data); + } + if (depth > 1 && metaData[depth - 2].tagStart === 'dict') { + metaData[depth - 2].data[metaData[depth - 1].key] = metaData[depth - 1].data; + } + depth--; + metaData.pop(); + metaData[depth].tagContent = ''; + metaData[depth].tagStart = ''; + metaData[depth].tagEnd = ''; + } + else { + if (metaData[depth].tagEnd === '/key' && metaData[depth].tagContent) { + metaData[depth].key = metaData[depth].tagContent; + } else { + if (metaData[depth].tagEnd === '/real' && metaData[depth].tagContent) { metaData[depth].data = parseFloat(metaData[depth].tagContent) || 0; } + if (metaData[depth].tagEnd === '/integer' && metaData[depth].tagContent) { metaData[depth].data = parseInt(metaData[depth].tagContent) || 0; } + if (metaData[depth].tagEnd === '/string' && metaData[depth].tagContent) { metaData[depth].data = metaData[depth].tagContent || ''; } + if (metaData[depth].tagEnd === '/boolean') { metaData[depth].data = metaData[depth].tagContent || false; } + if (metaData[depth].tagEnd === '/arrayEmpty') { metaData[depth].data = metaData[depth].tagContent || []; } + if (depth > 0 && metaData[depth - 1].tagStart === 'array') { metaData[depth - 1].data.push(metaData[depth].data); } + if (depth > 0 && metaData[depth - 1].tagStart === 'dict') { metaData[depth - 1].data[metaData[depth].key] = metaData[depth].data; } + } + metaData[depth].tagContent = ''; + metaData[depth].tagStart = ''; + metaData[depth].tagEnd = ''; } } + metaData[depth].tagEnd = ''; + inTagStart = false; + inTagContent = false; } - cpos = getNextTagPos(); + } else { + if (inTagStart) { metaData[depth].tagStart += c; } + if (inTagEnd) { metaData[depth].tagEnd += c; } + if (inTagContent) { metaData[depth].tagContent += c; } } - return (isArray ? arr : obj); - } - try { - const result = parseXmlTree(false, ''); - return result; - } catch (e) { - return {}; + pos++; } + return metaData[0].data; } function semverCompare(v1, v2) { diff --git a/lib/wifi.js b/lib/wifi.js index 5bf4147..bdc5465 100644 --- a/lib/wifi.js +++ b/lib/wifi.js @@ -315,8 +315,75 @@ function getWifiNetworkListIw(iface) { } } -function wifiNetworks(callback) { +/* + ssid: line.substring(parsedhead[0].from, parsedhead[0].to).trim(), + bssid: line.substring(parsedhead[1].from, parsedhead[1].to).trim().toLowerCase(), + mode: '', + channel, + frequency: wifiFrequencyFromChannel(channel), + signalLevel: signalLevel ? parseInt(signalLevel, 10) : null, + quality: wifiQualityFromDB(signalLevel), + security, + wpaFlags, + rsnFlags: [] + const securityAll = line.substring(parsedhead[6].from, 1000).trim().split(' '); + let security = []; + let wpaFlags = []; + securityAll.forEach(securitySingle => { + if (securitySingle.indexOf('(') > 0) { + const parts = securitySingle.split('('); + security.push(parts[0]); + wpaFlags = wpaFlags.concat(parts[1].replace(')', '').split(',')); + } + }); + +*/ +function parseWifiDarwin(wifiObj) { + const result = []; + wifiObj.forEach(function (wifiItem) { + const signalLevel = wifiItem.RSSI; + let security = []; + let wpaFlags = []; + if (wifiItem.WPA_IE) { + security.push('WPA'); + if (wifiItem.WPA_IE.IE_KEY_WPA_UCIPHERS) { + wifiItem.WPA_IE.IE_KEY_WPA_UCIPHERS.forEach(function (ciphers) { + if (ciphers === 0 && wpaFlags.indexOf('unknown/TKIP') === -1) { wpaFlags.push('unknown/TKIP'); } + if (ciphers === 2 && wpaFlags.indexOf('PSK/TKIP') === -1) { wpaFlags.push('PSK/TKIP'); } + if (ciphers === 4 && wpaFlags.indexOf('PSK/AES') === -1) { wpaFlags.push('PSK/AES'); } + }); + } + } + if (wifiItem.RSN_IE) { + security.push('WPA2'); + if (wifiItem.RSN_IE.IE_KEY_RSN_UCIPHERS) { + wifiItem.RSN_IE.IE_KEY_RSN_UCIPHERS.forEach(function (ciphers) { + if (ciphers === 0 && wpaFlags.indexOf('unknown/TKIP') === -1) { wpaFlags.push('unknown/TKIP'); } + if (ciphers === 2 && wpaFlags.indexOf('TKIP/TKIP') === -1) { wpaFlags.push('TKIP/TKIP'); } + if (ciphers === 4 && wpaFlags.indexOf('PSK/AES') === -1) { wpaFlags.push('PSK/AES'); } + }); + } + } + result.push({ + ssid: wifiItem.SSID_STR, + bssid: wifiItem.BSSID, + mode: '', + channel: wifiItem.CHANNEL, + frequency: wifiFrequencyFromChannel(wifiItem.CHANNEL), + signalLevel: signalLevel ? parseInt(signalLevel, 10) : null, + quality: wifiQualityFromDB(signalLevel), + security, + wpaFlags, + rsnFlags: [] + + }); + wifiItem.BSSID; + + }); + return result; +} +function wifiNetworks(callback) { return new Promise((resolve) => { process.nextTick(() => { let result = []; @@ -369,45 +436,10 @@ function wifiNetworks(callback) { resolve(result); } } else if (_darwin) { - let cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s'; - exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { - const lines = stdout.toString().split(os.EOL); - if (lines && lines.length > 1) { - const parsedhead = util.parseHead(lines[0], 1); - if (parsedhead.length >= 7) { - lines.shift(); - lines.forEach(line => { - if (line.trim()) { - const channelStr = line.substring(parsedhead[3].from, parsedhead[3].to).trim(); - const channel = channelStr ? parseInt(channelStr, 10) : null; - const signalLevel = line.substring(parsedhead[2].from, parsedhead[2].to).trim(); - const securityAll = line.substring(parsedhead[6].from, 1000).trim().split(' '); - let security = []; - let wpaFlags = []; - securityAll.forEach(securitySingle => { - if (securitySingle.indexOf('(') > 0) { - const parts = securitySingle.split('('); - security.push(parts[0]); - wpaFlags = wpaFlags.concat(parts[1].replace(')', '').split(',')); - } - }); - wpaFlags = Array.from(new Set(wpaFlags)); - result.push({ - ssid: line.substring(parsedhead[0].from, parsedhead[0].to).trim(), - bssid: line.substring(parsedhead[1].from, parsedhead[1].to).trim().toLowerCase(), - mode: '', - channel, - frequency: wifiFrequencyFromChannel(channel), - signalLevel: signalLevel ? parseInt(signalLevel, 10) : null, - quality: wifiQualityFromDB(signalLevel), - security, - wpaFlags, - rsnFlags: [] - }); - } - }); - } - } + let cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s -x'; + exec(cmd, { maxBuffer: 1024 * 40000 }, function (error, stdout) { + const output = stdout.toString(); + result = parseWifiDarwin(util.plistParser(output)); if (callback) { callback(result); }