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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9019eb4d32759d13110c74b96015eced6ee71a3536c1b9690da24d3a54e812d
4
- data.tar.gz: b5e805734cf53495aaeab2e1c67c7bb8104af0759980e2836d14c115dd15e5be
3
+ metadata.gz: 69ff4a48a042ef648c1cca4f2aca1722f5a56bfb8b4bd78ee6cc9f4ba732b9db
4
+ data.tar.gz: 0e5c99698650885c01a08caa740dfc5a4c5d35a8a2b0ff5c0eda31ce9297cf80
5
5
  SHA512:
6
- metadata.gz: 87872063dd2b7d1093a9340263ca742f2006d337dd43c6f2d30a4e12ba9985c3315b6072c9eb2d234f2b75e9b523d8f688a95f890e2feb7e567e4be7904a6bc7
7
- data.tar.gz: 9e84a5e8a3a69d425e6c7982571daf3f497e43b845cb5fc663b6c59227af2fda116e6196ad555df0b91b2cca9ed15086c8146052ac4c4e0613b3680ab21ca3de
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.7.0 at https://github.com/keithrbennett/wifiwand]
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 _verbose) on the command line.
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, the only gems used directly by the program are:
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`, to provide the interactive shell
331
- * `awesome_print` (optional), to more nicely format output in non-interactive mode
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 altogether as long as they don't need to use the interactive shell,
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 "\n\n#{'-' * 79}\nCommand: #{command}\n\n"
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
- # Because of a Mac issue which causes a request to hang if the network is turned
58
- # off during its lifetime, we give it only 5 seconds per try,
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
- tempfile = Tempfile.open('wifi-wand-')
84
+ if @verbose_mode
85
+ puts command_attempt_as_string("[Calling Net:HTTP.start(#{url.host})]")
86
+ end
77
87
 
78
88
  begin
79
- start_status_script = -> do
80
- script = "curl --silent --head https://www.google.com/ > /dev/null ; echo $? > #{tempfile.path} &"
81
- pid = Process.spawn(script)
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
- get_connected_state_from_curl = -> do
93
- tempfile.close
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 commands stdout as its sole parameter
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
- # Parses the XML text (using grep, not XML parsing) to find
121
- # <string> elements, and extracts the network name candidates
122
- # containing leading spaces from it.
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
- names.sort { |s1, s2| s1.casecmp(s2) } # sort alphabetically, case insensitively
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.strip
214
+ ssid_lines.empty? ? nil : ssid_lines.first.split('SSID: ').last.lstrip
259
215
  end
260
216
 
261
217
 
@@ -1,5 +1,5 @@
1
1
  module WifiWand
2
2
 
3
- VERSION = '2.7.0'
3
+ VERSION = '2.8.0'
4
4
 
5
5
  end
@@ -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.7.0
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-14 00:00:00.000000000 Z
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: