(raw)
| 123 | } |
| 124 | |
| 125 | function parseExitNodeList(raw) { |
| 126 | var lines = String(raw || "").split(/\r?\n/) |
| 127 | var header = "" |
| 128 | var headerIndex = -1 |
| 129 | for (var i = 0; i < lines.length; i++) { |
| 130 | if (/^\s*IP\s+HOSTNAME\s+COUNTRY\s+CITY\s+STATUS\s*$/.test(lines[i])) { |
| 131 | header = lines[i] |
| 132 | headerIndex = i |
| 133 | break |
| 134 | } |
| 135 | } |
| 136 | if (headerIndex === -1) return [] |
| 137 | |
| 138 | var ipStart = header.indexOf("IP") |
| 139 | var hostStart = header.indexOf("HOSTNAME") |
| 140 | var countryStart = header.indexOf("COUNTRY") |
| 141 | var cityStart = header.indexOf("CITY") |
| 142 | var statusStart = header.indexOf("STATUS") |
| 143 | var byHost = {} |
| 144 | |
| 145 | for (var j = headerIndex + 1; j < lines.length; j++) { |
| 146 | var line = lines[j] |
| 147 | if (/^\s*$/.test(line) || /^\s*#/.test(line)) continue |
| 148 | |
| 149 | var ip = sliceTableColumn(line, ipStart, hostStart) |
| 150 | var host = sliceTableColumn(line, hostStart, countryStart) |
| 151 | var country = sliceTableColumn(line, countryStart, cityStart) |
| 152 | var city = sliceTableColumn(line, cityStart, statusStart) |
| 153 | var status = sliceTableColumn(line, statusStart, -1) |
| 154 | if (!isMullvadHost(host)) continue |
| 155 | |
| 156 | byHost[host] = { |
| 157 | id: "mullvad:" + host, |
| 158 | HostName: host, |
| 159 | DNSName: host, |
| 160 | DisplayName: (city && city !== "Any" ? city + ", " : "") + country, |
| 161 | TailscaleIPs: ip ? [ip] : [], |
| 162 | TailscaleIPv6: [], |
| 163 | Online: true, |
| 164 | OS: "mullvad", |
| 165 | Tags: [], |
| 166 | ExitNodeOption: true, |
| 167 | ExitNode: status !== "" && status !== "-", |
| 168 | Mullvad: true, |
| 169 | Country: country, |
| 170 | City: city, |
| 171 | Status: status |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | var result = [] |
| 176 | for (var hostName in byHost) result.push(byHost[hostName]) |
| 177 | result.sort(function(a, b) { |
| 178 | var countryCompare = String(a.Country).localeCompare(String(b.Country)) |
| 179 | if (countryCompare !== 0) return countryCompare |
| 180 | return String(a.DisplayName).localeCompare(String(b.DisplayName)) |
| 181 | }) |
| 182 | return result |
nothing calls this directly
no test coverage detected