wifi-wand 2.7.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +23 -7
- data/RELEASE_NOTES.md +7 -0
- data/lib/wifi-wand/command_line_interface.rb +1 -5
- data/lib/wifi-wand/models/base_model.rb +41 -62
- data/lib/wifi-wand/models/mac_os_model.rb +13 -57
- data/lib/wifi-wand/version.rb +1 -1
- data/test-data/invalid-byte-sequence-network-names.txt +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69ff4a48a042ef648c1cca4f2aca1722f5a56bfb8b4bd78ee6cc9f4ba732b9db
|
4
|
+
data.tar.gz: 0e5c99698650885c01a08caa740dfc5a4c5d35a8a2b0ff5c0eda31ce9297cf80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5c2067026a31c0e0b976d5d5c63f2680d7324838583060d70d50c9289d3f360ea4c11210ebb062803a680dffadb9996fad2a65c0027c7524318b6bc27942c45
|
7
|
+
data.tar.gz: aeedfe2437d7f11cb8ad3dbde4cebc63ad518f72f5e4bd52106850e8c856364471ffc036a89f12755748fc95ed835a2965d4da832b470534f3e3ae3009349225
|
data/README.md
CHANGED
@@ -28,7 +28,7 @@ output at the time of this writing:
|
|
28
28
|
```
|
29
29
|
$ wifi-wand -h
|
30
30
|
|
31
|
-
Command Line Switches: [wifi-wand version 2.
|
31
|
+
Command Line Switches: [wifi-wand version 2.8.0 at https://github.com/keithrbennett/wifiwand]
|
32
32
|
|
33
33
|
-o {i,j,k,p,y} - outputs data in inspect, JSON, pretty JSON, puts, or YAML format when not in shell mode
|
34
34
|
-p wifi_port_name - override automatic detection of port name with this name
|
@@ -88,14 +88,14 @@ version of Ruby that comes packaged with MacOS.
|
|
88
88
|
### JSON, YAML, and Other Output Formats
|
89
89
|
|
90
90
|
You can specify that output in _noninteractive_ mode be in a certain format.
|
91
|
-
Currently, JSON, YAML, inspect, and puts formats are supported.
|
91
|
+
Currently, JSON, "Pretty" JSON, YAML, inspect, and puts formats are supported.
|
92
92
|
See the help for which command line switches to use.
|
93
93
|
|
94
94
|
|
95
95
|
### Seeing the Underlying OS Commands and Output
|
96
96
|
|
97
97
|
If you would like to see the Mac OS commands and their output,
|
98
|
-
you can do so by specifying "-v" (for
|
98
|
+
you can do so by specifying "-v" (for _verbose_) on the command line.
|
99
99
|
|
100
100
|
You may notice that some commands are executed more than once. This is to simplify the application logic
|
101
101
|
and eliminate the need for the complexity of balancing the speed that a cache offers and the risk
|
@@ -325,15 +325,31 @@ Connected!
|
|
325
325
|
|
326
326
|
### Dependent Gems
|
327
327
|
|
328
|
-
Currently,
|
328
|
+
Currently, no dependent gems are installed when this gem is installed.
|
329
|
+
However, the program _will_ use other gems as follows:
|
329
330
|
|
330
|
-
* `pry`,
|
331
|
-
* `awesome_print
|
331
|
+
* `pry`, when the interactive shell is requested with the `-s` option
|
332
|
+
* `awesome_print`, if it is found, to more nicely format output in non-interactive mode
|
332
333
|
|
333
|
-
So the user can avoid installing gems
|
334
|
+
So the user can avoid installing gems other than this one altogether
|
335
|
+
as long as they don't need to use the interactive shell,
|
334
336
|
and as long as they are comfortable with the less pretty output.
|
335
337
|
|
336
338
|
|
339
|
+
### Public IP Information
|
340
|
+
|
341
|
+
The information hash will normally include information about the public IP address.
|
342
|
+
However, the command that provides this information, `curl -s ipinfo.io`, will sometimes
|
343
|
+
return this:
|
344
|
+
|
345
|
+
`Rate limit exceeded. Subscribe to a paid plan to increase your usage limits`
|
346
|
+
|
347
|
+
If this happens, the public IP information will be silently omitted from the
|
348
|
+
information hash. In this case, the web site 'https://www.whatismyip.com' is
|
349
|
+
recommended, and `ro ipw` on the command line or `ro 'ipw'` in the shell will
|
350
|
+
open that page in your browser for you.
|
351
|
+
|
352
|
+
|
337
353
|
### Password Lookup Oddity
|
338
354
|
|
339
355
|
You may find it odd (I did, anyway) that even if you issue the password command
|
data/RELEASE_NOTES.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## v2.8.0
|
2
|
+
|
3
|
+
* Substantial simplifications of model implementations of connected_to_internet?, available_network_names.
|
4
|
+
* Fixed network name reporting problems regarding leading/trailing spaces.
|
5
|
+
* Improve verbose output by printing command when issued, not after completed.
|
6
|
+
|
7
|
+
|
1
8
|
## v2.7.0
|
2
9
|
|
3
10
|
* Fix models not being loadable after requiring the gem.
|
@@ -374,6 +374,7 @@ When in interactive shell mode:
|
|
374
374
|
model.open_resource(resource.resource)
|
375
375
|
end
|
376
376
|
end
|
377
|
+
nil
|
377
378
|
end
|
378
379
|
|
379
380
|
|
@@ -404,11 +405,6 @@ When in interactive shell mode:
|
|
404
405
|
end
|
405
406
|
|
406
407
|
|
407
|
-
def cmd_pu
|
408
|
-
`open https://www.whatismyip.com/`
|
409
|
-
end
|
410
|
-
|
411
|
-
|
412
408
|
def cmd_q
|
413
409
|
quit
|
414
410
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'net/http'
|
2
3
|
require 'tempfile'
|
4
|
+
require 'uri'
|
3
5
|
require_relative '../../wifi-wand'
|
4
6
|
|
5
7
|
module WifiWand
|
@@ -29,6 +31,7 @@ class BaseModel
|
|
29
31
|
|
30
32
|
def initialize(options)
|
31
33
|
@verbose_mode = options.verbose
|
34
|
+
|
32
35
|
if options.wifi_port && (! is_wifi_port?(options.wifi_port))
|
33
36
|
raise "#{options.wifi_port} is not a Wi-Fi interface."
|
34
37
|
end
|
@@ -36,13 +39,30 @@ class BaseModel
|
|
36
39
|
end
|
37
40
|
|
38
41
|
|
42
|
+
def banner_line
|
43
|
+
@banner_line ||= '-' * 79
|
44
|
+
end
|
45
|
+
|
46
|
+
def command_attempt_as_string(command)
|
47
|
+
"\n\n#{banner_line}\nCommand: #{command}\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
def command_result_as_string(output)
|
51
|
+
"#{output}#{banner_line}\n\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
39
56
|
def run_os_command(command, raise_on_error = true)
|
40
57
|
|
58
|
+
if @verbose_mode
|
59
|
+
puts command_attempt_as_string(command)
|
60
|
+
end
|
61
|
+
|
41
62
|
output = `#{command} 2>&1` # join stderr with stdout
|
42
63
|
|
43
64
|
if @verbose_mode
|
44
|
-
puts
|
45
|
-
puts "#{output}#{'-' * 79}\n\n"
|
65
|
+
puts command_result_as_string(output)
|
46
66
|
end
|
47
67
|
|
48
68
|
if $?.exitstatus != 0 && raise_on_error
|
@@ -53,73 +73,32 @@ class BaseModel
|
|
53
73
|
end
|
54
74
|
|
55
75
|
|
56
|
-
# This method returns whether or not there is a working Internet connection
|
57
|
-
#
|
58
|
-
#
|
59
|
-
# and limit the number of tries to 3.
|
60
|
-
#
|
61
|
-
# This implementation will probably strike you as overly complex. The following
|
62
|
-
# code looks like it is all that should be necessary, but unfortunately
|
63
|
-
# this implementation often hangs when wifi is turned off while curl is active
|
64
|
-
#
|
65
|
-
# def connected_to_internet?
|
66
|
-
# script = "curl --silent --head http://www.google.com/ > /dev/null ; echo $?"
|
67
|
-
# result = `#{script}`.chomp
|
68
|
-
# puts result
|
69
|
-
# result == '0'
|
70
|
-
# end
|
71
|
-
|
72
|
-
# TODO Investigate using Curl options: --connect-timeout 1 --max-time 2 --retry 0
|
73
|
-
# to greatly simplify this method.
|
76
|
+
# This method returns whether or not there is a working Internet connection,
|
77
|
+
# which is defined as being able to get a successful response
|
78
|
+
# from google.com within 3 seconds..
|
74
79
|
def connected_to_internet?
|
80
|
+
test_site = 'https://www.google.com'
|
81
|
+
url = URI.parse(test_site)
|
82
|
+
success = true
|
75
83
|
|
76
|
-
|
84
|
+
if @verbose_mode
|
85
|
+
puts command_attempt_as_string("[Calling Net:HTTP.start(#{url.host})]")
|
86
|
+
end
|
77
87
|
|
78
88
|
begin
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
Process.detach(pid)
|
83
|
-
pid
|
84
|
-
end
|
85
|
-
|
86
|
-
process_is_running = ->(pid) do
|
87
|
-
script = %Q{ps -p #{pid} > /dev/null; echo $?}
|
88
|
-
output = `#{script}`.chomp
|
89
|
-
output == "0"
|
89
|
+
Net::HTTP.start(url.host) do |http|
|
90
|
+
http.read_timeout = 3 # seconds
|
91
|
+
http.get('.')
|
90
92
|
end
|
93
|
+
rescue
|
94
|
+
success = false
|
95
|
+
end
|
91
96
|
|
92
|
-
|
93
|
-
|
94
|
-
File.read(tempfile.path).chomp == '0'
|
95
|
-
end
|
96
|
-
|
97
|
-
# Do one run, iterating during the timeout period to see if the command has completed
|
98
|
-
do_one_run = -> do
|
99
|
-
end_time = Time.now + 3
|
100
|
-
pid = start_status_script.()
|
101
|
-
while Time.now < end_time
|
102
|
-
if process_is_running.(pid)
|
103
|
-
sleep 0.5
|
104
|
-
else
|
105
|
-
return get_connected_state_from_curl.()
|
106
|
-
end
|
107
|
-
end
|
108
|
-
Process.kill('KILL', pid) if process_is_running.(pid)
|
109
|
-
:hung
|
110
|
-
end
|
111
|
-
|
112
|
-
3.times do
|
113
|
-
connected = do_one_run.()
|
114
|
-
return connected if connected != :hung
|
115
|
-
end
|
116
|
-
|
117
|
-
raise "Could not determine Internet status."
|
118
|
-
|
119
|
-
ensure
|
120
|
-
tempfile.unlink
|
97
|
+
if @verbose_mode
|
98
|
+
puts command_result_as_string("#{success}\n")
|
121
99
|
end
|
122
100
|
|
101
|
+
success
|
123
102
|
end
|
124
103
|
|
125
104
|
|
@@ -225,7 +204,7 @@ class BaseModel
|
|
225
204
|
|
226
205
|
# Tries an OS command until the stop condition is true.
|
227
206
|
# @command the command to run in the OS
|
228
|
-
# @stop_condition a lambda taking the
|
207
|
+
# @stop_condition a lambda taking the command's stdout as its sole parameter
|
229
208
|
# @return the stdout produced by the command
|
230
209
|
def try_os_command_until(command, stop_condition, max_tries = 100)
|
231
210
|
max_tries.times do
|
@@ -19,6 +19,7 @@ class MacOsModel < BaseModel
|
|
19
19
|
# Identifies the (first) wireless network hardware port in the system, e.g. en0 or en1
|
20
20
|
# This may not detect wifi ports with nonstandard names, such as USB wifi devices.
|
21
21
|
def detect_wifi_port
|
22
|
+
|
22
23
|
lines = run_os_command("networksetup -listallhardwareports").split("\n")
|
23
24
|
# Produces something like this:
|
24
25
|
# Hardware Port: Wi-Fi
|
@@ -28,9 +29,11 @@ class MacOsModel < BaseModel
|
|
28
29
|
# Hardware Port: Bluetooth PAN
|
29
30
|
# Device: en3
|
30
31
|
# Ethernet Address: ac:bc:32:b9:a9:9e
|
32
|
+
|
31
33
|
wifi_port_line_num = (0...lines.size).detect do |index|
|
32
34
|
/: Wi-Fi$/.match(lines[index])
|
33
35
|
end
|
36
|
+
|
34
37
|
if wifi_port_line_num.nil?
|
35
38
|
raise %Q{Wifi port (e.g. "en0") not found in output of: networksetup -listallhardwareports}
|
36
39
|
else
|
@@ -89,68 +92,21 @@ class MacOsModel < BaseModel
|
|
89
92
|
end
|
90
93
|
|
91
94
|
|
92
|
-
def parse_network_names(info)
|
93
|
-
if info.nil?
|
94
|
-
nil
|
95
|
-
else
|
96
|
-
info[1..-1] \
|
97
|
-
.map { |line| line[0..32].rstrip } \
|
98
|
-
.uniq \
|
99
|
-
.sort { |s1, s2| s1.casecmp(s2) }
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
|
104
95
|
# @return an array of unique available network names only, sorted alphabetically
|
105
|
-
# Kludge alert: the tabular data does not differentiate between strings with and without leading whitespace
|
106
|
-
# Therefore, we get the data once in tabular format, and another time in XML format.
|
107
|
-
# The XML element will include any leading whitespace. However, it includes all <string> elements,
|
108
|
-
# many of which are not network names.
|
109
|
-
# As an improved approximation of the correct result, for each network name found in tabular mode,
|
110
|
-
# we look to see if there is a corresponding string element with leading whitespace, and, if so,
|
111
|
-
# replace it.
|
112
|
-
#
|
113
|
-
# This will not behave correctly if a given name has occurrences with different amounts of whitespace,
|
114
|
-
# e.g. ' x' and ' x'.
|
115
|
-
#
|
116
96
|
# The reason we don't use an XML parser to get the exactly correct result is that we don't want
|
117
97
|
# users to need to install any external dependencies in order to run this script.
|
118
98
|
def available_network_names
|
119
99
|
|
120
|
-
#
|
121
|
-
|
122
|
-
|
123
|
-
get_leading_space_names = ->(text) do
|
124
|
-
text.split("\n") \
|
125
|
-
.grep(%r{<string>}) \
|
126
|
-
.sort \
|
127
|
-
.uniq \
|
128
|
-
.map { |line| line.gsub("<string>", '').gsub('</string>', '').gsub("\t", '') } \
|
129
|
-
.select { |s| s[0] == ' ' }
|
130
|
-
end
|
131
|
-
|
132
|
-
|
133
|
-
output_is_valid = ->(output) { ! ([nil, ''].include?(output)) }
|
134
|
-
tabular_data = try_os_command_until("#{AIRPORT_CMD} -s", output_is_valid)
|
135
|
-
xml_data = try_os_command_until("#{AIRPORT_CMD} -s -x", output_is_valid)
|
136
|
-
|
137
|
-
if tabular_data.nil? || xml_data.nil?
|
138
|
-
raise "Unable to get available network information; please try again."
|
139
|
-
end
|
140
|
-
|
141
|
-
tabular_data_lines = tabular_data[1..-1] # omit header line
|
142
|
-
names_no_spaces = parse_network_names(tabular_data_lines.split("\n")).map(&:strip)
|
143
|
-
names_maybe_spaces = get_leading_space_names.(xml_data)
|
144
|
-
|
145
|
-
names = names_no_spaces.map do |name_no_spaces|
|
146
|
-
match = names_maybe_spaces.detect do |name_maybe_spaces|
|
147
|
-
%r{[ \t]?#{name_no_spaces}$}.match(name_maybe_spaces)
|
148
|
-
end
|
149
|
-
|
150
|
-
match ? match : name_no_spaces
|
151
|
-
end
|
100
|
+
# awk command below kindly provided by @nohillside at https://apple.stackexchange.com/questions/320323/how-to-programmatically-get-available-wifi-networks-without-airport-utility.
|
101
|
+
command = %q{airport -s -x | awk '{ if (catch == 1) { print; catch=0 } } /SSID_STR/ { catch=1 }'}
|
102
|
+
stop_condition = ->(response) { ! [nil, ''].include?(response) }
|
152
103
|
|
153
|
-
|
104
|
+
output = try_os_command_until(command, stop_condition)
|
105
|
+
output = output.split("\n")
|
106
|
+
output.map!(&:strip)
|
107
|
+
output.map! { |s| s.gsub(/<\/?string>/, '') }
|
108
|
+
output.sort! { |s1, s2| s1.casecmp(s2) } # sort alphabetically, case insensitively
|
109
|
+
output.uniq!
|
154
110
|
end
|
155
111
|
|
156
112
|
|
@@ -255,7 +211,7 @@ class MacOsModel < BaseModel
|
|
255
211
|
def connected_network_name
|
256
212
|
lines = run_os_command("#{AIRPORT_CMD} -I").split("\n")
|
257
213
|
ssid_lines = lines.grep(/ SSID:/)
|
258
|
-
ssid_lines.empty? ? nil : ssid_lines.first.split('SSID: ').last.
|
214
|
+
ssid_lines.empty? ? nil : ssid_lines.first.split('SSID: ').last.lstrip
|
259
215
|
end
|
260
216
|
|
261
217
|
|
data/lib/wifi-wand/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
"\t\t<string>Tenants</string>\n\t\t<string>iPad</string>\n\t\t<string>Robinsonsmalls</string>\n\t\t<string>Counter_07959</string>\n\t\t<string>Tenants</string>\n\t\t<string>RDS</string>\n\t\t<string>Lenovo PHAB2</string>\n\t\t<string>SHAKEYS FREE WIFI</string>\n\t\t<string>Meldrick\xD5s iPhone</string>\n\t\t<string>quikstop cyber</string>\n\t\t<string>PLDTHOMEDSL</string>\n\t\t<string>Robinsonsmalls</string>\n\t\t<string>Tenants</string>\n\t\t<string>Robinsonsmalls</string>\n\t\t<string>islandtea</string>\n\t\t<string>PLDTHOMEDSL52919</string>\n\t\t<string>PLDTHOMEDSLCHEF</string>\n\t\t<string>PLDTMyDSLBiz5G06370</string>\n\t\t<string>AustinPowrovich</string>\n"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wifi-wand
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith Bennett
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,6 +80,7 @@ files:
|
|
80
80
|
- sample-available-networks.json
|
81
81
|
- sample-available-networks.yaml
|
82
82
|
- spec/wifi-wand/models/mac_os_model_spec.rb
|
83
|
+
- test-data/invalid-byte-sequence-network-names.txt
|
83
84
|
- wifi-wand.gemspec
|
84
85
|
homepage: https://github.com/keithrbennett/wifi-wand
|
85
86
|
licenses:
|