Imported Upstream version 2.0
Devon Kearns
11 years ago
0 | Pipal, Password Analyser | |
1 | ======================== | |
2 | ||
3 | Copyright(c) 2012, Robin Wood <[email protected]> | |
4 | ||
5 | On most internal pen-tests I do I generally manage to get a password dump from | |
6 | the DC. To do some basic analysis on this I wrote Counter and since I originally | |
7 | released it I've made quite a few mods to it to generate extra stats that are | |
8 | useful when doing reports to management. | |
9 | ||
10 | Recently a good friend, n00bz, asked on Twitter if anyone had a tool that he | |
11 | could use to analyse some passwords he had. I pointed him to Counter and said if | |
12 | he had any suggestions for additions to let me know. He did just that and over | |
13 | the last month between us we have come up with a load of new features which we | |
14 | both think will help anyone with a large dump of cracked passwords to analyse. | |
15 | We also got some input from well known password analysts Matt Weir and Martin | |
16 | Bos who I'd like to give a big thanks to. | |
17 | ||
18 | I have to point out before going on, all this tool does is to give you the stats | |
19 | and the information to help you analyse the passwords. The real work is done by | |
20 | you in interpreting the results, I give you the numbers, you tell the story. | |
21 | ||
22 | Seeing as there have been so many changes to the underlying code I also decided | |
23 | to change the name (see below) and do a full new release. | |
24 | ||
25 | So, what does this new version do? The best way to describe it is to see some | |
26 | examples so go to the Pipal project page at www.digininja.org/projects/pipal.php | |
27 | for a full walk through of a sample analysis. | |
28 | ||
29 | Install / Usage | |
30 | =============== | |
31 | The app will only work with Ruby 1.9.x, if you try to run it in any previous | |
32 | versions you will get a warning and the app will close. | |
33 | ||
34 | Pipal is completely self contained and requires no gems installing so should | |
35 | work on any vanilla Ruby install. | |
36 | ||
37 | Usage is fairly simple, -? will give you full instructions: | |
38 | ||
39 | $ ./pipal.rb -? | |
40 | pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
41 | ||
42 | Usage: pipal [OPTION] ... FILENAME | |
43 | --help, -h: show help | |
44 | --top, -t X: show the top X results (default 10) | |
45 | --output, -o : output to file | |
46 | --external, -e : external file to compare words against | |
47 | --gkey <Google Maps API key>: to allow zip code lookups (optional) | |
48 | ||
49 | FILENAME: The file to count | |
50 | ||
51 | When you run the app you'll get a nice progress bar which gives you a rough idea | |
52 | of how long the app will take to run. If you want to stop it at any point | |
53 | hitting ctrl-c will stop the parsing and will dump out the stats generated so | |
54 | far. | |
55 | ||
56 | The progress bar is based on a line count from the file which it gets this using | |
57 | the wc command. If it can't find wc it will make a guess at the number of lines | |
58 | based on the file size and an average line length of 8 bytes so the progress bar | |
59 | may not be fully accurate but should still give you an idea. | |
60 | ||
61 | The Google Maps API key is supposed to be used by Google to only allow access to | |
62 | their API to registered users. I assumed this was true and registered for a key | |
63 | but in putting together this release I found that it will take any value and | |
64 | still do the look up. This may be a bug at the Google end or deliberate and may | |
65 | change any any time so I'd suggest grabbing a key just in case. To use it you | |
66 | can either edit the script and put the key into the constant on line 35 or you | |
67 | can pass it on the command line every time. If you are going to hope that you | |
68 | don't need a valid key then just put X in as the value as without something | |
69 | Pipal won't try to perform a look up. | |
70 | ||
71 | Version History | |
72 | =============== | |
73 | ||
74 | Version 2 - Two big changes, the first a massive speed increase. This patch was | |
75 | submitted by Stefan Venken who said a small mention would be good enough, I want | |
76 | to give him a big mention. Running through the LinkedIn lists would have taken | |
77 | many many hours on version 1, version 2 went through 3.5 million records in | |
78 | about 15 minutes. Thank you. | |
79 | ||
80 | Second change is the addition of US area and zip code lookups. This little | |
81 | feature gives some interesting geographical data when ran across password lists | |
82 | originating in the US. The best example I've seen of this is the dump from the | |
83 | Military Singles site where some passwords could be obviously seen to be grouped | |
84 | around US military bases. People in the UK don't have the same relationship with | |
85 | phone numbers so I know this won't work here but if anyone can suggest any other | |
86 | areas where this might be useful then I'll look at building in some kind of | |
87 | location awareness feature so you can specify the source of the list and get | |
88 | results customized to the correct area or just run every area and see if a | |
89 | pattern emerges. | |
90 | ||
91 | A non-code-base change is for version 2 is the move from hosting the code myself | |
92 | to github. This is my first github hosted project so I may get things wrong, if | |
93 | I do, sorry. A number of people asked how they could submit patches so this | |
94 | seems like the best way to do it, lets hope it works out. | |
95 | ||
96 | Version 1 - Was a proof of concept, written fairly in a fairly verbose way so not | |
97 | very optimised. Took off way more than I expected it would and gathered a lot of | |
98 | community support. | |
99 | ||
100 | Feedback/Todo | |
101 | ============= | |
102 | ||
103 | If you have a read through the source for Pipal you'll notice that it isn't very | |
104 | efficient at the moment. The way I built it was to try to keep each chunk of | |
105 | stats together as a distinct group so that if I wanted to add a new, similar, | |
106 | group then it was easy to just copy and paste the group. Now I've got a working | |
107 | app and I know roughly what I need in the different group types I've got an idea | |
108 | on how to rewrite the main parser to make it much more efficient and hopefully | |
109 | multi-threaded which should speed up the processing by a lot for large lists. | |
110 | ||
111 | I could have made these changes before releasing version 1.0 but I figured | |
112 | before I do I want to get as much feedback as possible from users about the | |
113 | features already implemented and about any new features they would like to see | |
114 | so that I can bundle all these together into version 2. So, please get in touch | |
115 | if there is a set of stats that you'd like to see included. | |
116 | ||
117 | One other thing I know needs fixing, Pipal doesn't handle certain character | |
118 | encodings very well. If anyone knows how to correctly deal with different | |
119 | encoding types, especially with regards to regular expressions, please let me | |
120 | know. | |
121 | ||
122 | Licence | |
123 | ======= | |
124 | This project released under the Creative Commons Attribution-Share Alike 2.0 | |
125 | UK: England & Wales | |
126 | ||
127 | ( http://creativecommons.org/licenses/by-sa/2.0/uk/ ) |
0 | # This is a slightly modified version of the HorizBar script taken from | |
1 | # https://blogs.oracle.com/realneel/entry/ascii_graphs_using_ruby | |
2 | ||
3 | class HorizBar | |
4 | WIDTH = 72 | |
5 | HEIGHT = 16 | |
6 | attr :output_file, true | |
7 | ||
8 | def initialize(array) | |
9 | @values = array | |
10 | @output_file = STDOUT | |
11 | end | |
12 | ||
13 | def draw | |
14 | #Adjust X axis when there are more than WIDTH cols | |
15 | if @values.length > WIDTH then | |
16 | old_values = @values; | |
17 | @values = [] | |
18 | 0.upto(WIDTH - 1){ |i| @values << old_values[i*old_values.length/WIDTH]} | |
19 | end | |
20 | ||
21 | max = 0 | |
22 | @values.each do |val| | |
23 | if !val.nil? and max < val | |
24 | max = val | |
25 | end | |
26 | end | |
27 | # can't use this as the array can have nil's in it | |
28 | # and max can't cope with that | |
29 | # max = @values.max | |
30 | if max == 0 | |
31 | return | |
32 | end | |
33 | ||
34 | # initialize display with blanks | |
35 | display = Array.new(HEIGHT).collect { Array.new(WIDTH, ' ') } | |
36 | @values.each_with_index do |e, i| | |
37 | f = (e.nil?)?0:e | |
38 | ||
39 | num= f*HEIGHT/max | |
40 | (HEIGHT - 1).downto(HEIGHT - 1 - num){|j| display[j][i] = '|'} | |
41 | end | |
42 | display.each{|ar| ar.each{|e| @output_file.putc e}; @output_file.puts "\n"} #now print | |
43 | ||
44 | no_of_digits = (@values.length - 1).to_s.length | |
45 | 0.upto(no_of_digits) do |digit_number| | |
46 | 0.upto(@values.length - 1) do |x| | |
47 | @output_file.print sprintf("%0#{no_of_digits}d", x)[digit_number] | |
48 | end | |
49 | @output_file.puts | |
50 | end | |
51 | end | |
52 | end |
0 | #!/usr/bin/env ruby | |
1 | ||
2 | # == Pipal: Statistical analysis on password dumps | |
3 | # | |
4 | # == Usage | |
5 | # | |
6 | # Usage: pipal [OPTION] ... FILENAME | |
7 | # --help, -h: show help | |
8 | # --top, -t X: show the top X results (default 10) | |
9 | # --output, -o <filename>: output to file | |
10 | # --external, -e <filename>: external file to compare words against | |
11 | # --gkey <Google Maps API key>: to allow zip code lookups (optional) | |
12 | # | |
13 | # FILENAME: The file to count | |
14 | # | |
15 | # Author:: Robin Wood ([email protected]) | |
16 | # Copyright:: Copyright (c) Robin Wood 2011 | |
17 | # Licence:: Creative Commons Attribution-Share Alike 2.0 | |
18 | # Speedbumped by Stefan Venken ([email protected]) | |
19 | # | |
20 | ||
21 | require 'benchmark' | |
22 | require 'getoptlong' | |
23 | require'net/http' | |
24 | require'uri' | |
25 | require'json' | |
26 | require_relative './horizbar' | |
27 | require_relative './progressbar' | |
28 | require_relative './us_area_codes.rb' | |
29 | ||
30 | # Place your google API key here | |
31 | # For more info on getting a key see here https://developers.google.com/maps/documentation/javascript/tutorial#api_key | |
32 | # If you want to leave this blank and pass the key on the command line you can use the --gkey option | |
33 | # From experiments it looks like you don't actually need a valid key but better to have one just in case | |
34 | GOOGLE_API_KEY='' | |
35 | ||
36 | if RUBY_VERSION =~ /1\.8/ | |
37 | puts "Sorry, Pipal only works correctly on Ruby 1.9.x." | |
38 | puts | |
39 | exit | |
40 | end | |
41 | ||
42 | trap("SIGINT") { throw :ctrl_c } | |
43 | ||
44 | time = Benchmark.measure do | |
45 | ||
46 | days_ab = {'mon' => 0, 'tues' => 0, 'wed' => 0, 'thurs' => 0, 'fri' => 0, 'sat' => 0, 'sun' => 0} | |
47 | months_ab = {"jan" => 0, "feb" => 0, "mar" => 0, "apr" => 0, "may" => 0, "jun" => 0, "jul" => 0, "aug" => 0, "sept" => 0, "oct" => 0, "nov" => 0, "dec" => 0} | |
48 | ||
49 | colours = {"black" => 0, "blue" => 0, "brown" => 0, "gray" => 0, "green" => 0, "orange" => 0, "pink" => 0, "purple" => 0, "red" => 0, "white" => 0, "yellow" => 0, 'violet' => 0, 'indigo' => 0} | |
50 | days = {'monday' => 0, 'tuesday' => 0, 'wednesday' => 0, 'thursday' => 0, 'friday' => 0, 'saturday' => 0, 'sunday' => 0} | |
51 | months = {"january" => 0, "february" => 0, "march" => 0, "april" => 0, "may" => 0, "june" => 0, "july" => 0, "august" => 0, "september" => 0, "october" => 0, "november" => 0, "december" => 0} | |
52 | ||
53 | char_stats = { | |
54 | "loweralpha" => {'regex' => /^[a-z]+$/, "count" => 0}, | |
55 | "upperalpha" => {'regex' => /^[A-Z]+$/, "count" => 0}, | |
56 | "numeric" => {'regex' => /^[0-9]+$/, "count" => 0}, | |
57 | "special" => {'regex' => /^[\p{Punct}]+$/, "count" => 0}, | |
58 | ||
59 | "loweralphanum" => {'regex' => /^[a-z0-9]+$/, "count" => 0}, | |
60 | "upperalphanum" => {'regex' => /^[A-Z0-9]+$/, "count" => 0}, | |
61 | "mixedalpha" => {'regex' => /^[a-zA-Z]+$/, "count" => 0}, | |
62 | "loweralphaspecial" => {'regex' => /^[a-z\p{Punct}]+$/, "count" => 0}, | |
63 | "upperalphaspecial" => {'regex' => /^[A-Z\p{Punct}]+$/, "count" => 0}, | |
64 | "specialnum" => {'regex' => /^[\p{Punct}0-9]+$/, "count" => 0}, | |
65 | ||
66 | "mixedalphanum" => {'regex' => /^[a-zA-Z0-9]+$/, "count" => 0}, | |
67 | "loweralphaspecialnum" => {'regex' => /^[a-z\p{Punct}0-9]+$/, "count" => 0}, | |
68 | "mixedalphaspecial" => {'regex' => /^[A-Za-z\p{Punct}]+$/, "count" => 0}, | |
69 | "upperalphaspecialnum" => {'regex' => /^[A-Z\p{Punct}0-9]+$/, "count" => 0}, | |
70 | ||
71 | "mixedalphaspecialnum" => {'regex' => /^[A-Za-z\p{Punct}0-9]+$/, "count" => 0}, | |
72 | } | |
73 | ||
74 | char_sets_ordering = { | |
75 | "stringdigit" => {"regex" => /^[a-z]+[0-9]+$/, "count" => 0}, | |
76 | "allstring" => {"regex" => /^[a-z]+$/, "count" => 0}, | |
77 | "digitstring" => {"regex" => /^[0-9]+[a-z]+$/, "count" => 0}, | |
78 | "stringdigitstring" => {"regex" => /^[a-z]+[0-9]+[a-z]+$/, "count" => 0}, | |
79 | "alldigit" => {"regex" => /^[0-9]+$/, "count" => 0}, | |
80 | "digitstringdigit" => {"regex" => /^[0-9]+[a-z]+[0-9]+$/, "count" => 0}, | |
81 | "stringspecialdigit" => {"regex" => /^[a-z]+[\p{Punct}]+[0-9]+$/, "count" => 0}, | |
82 | "stringspecialstring" => {"regex" => /^[a-z]+[\p{Punct}]+[a-z]+$/, "count" => 0}, | |
83 | "stringspecial" => {"regex" => /^[a-z]+[\p{Punct}]+$/, "count" => 0}, | |
84 | "specialstring" => {"regex" => /^[\p{Punct}]+[a-z]+$/, "count" => 0}, | |
85 | "specialstringspecial" => {"regex" => /^[\p{Punct}]+[a-z]+[\p{Punct}]+$/, "count" => 0}, | |
86 | "allspecial" => {"regex" => /^[\p{Punct}]+$/, "count" => 0}, | |
87 | "othermask" => {"regex" => /^.*$/, "count" => 0} | |
88 | } | |
89 | ||
90 | hashcat_masks = {} | |
91 | ||
92 | words = {} | |
93 | total_lines = 0 | |
94 | ||
95 | one_to_six_chars = 0 | |
96 | one_to_eight_chars = 0 | |
97 | over_eight_chars = 0 | |
98 | #only_lower_alpha_chars = 0 | |
99 | #only_upper_alpha_chars = 0 | |
100 | #only_numeric_chars = 0 | |
101 | ||
102 | first_cap_last_num = 0 | |
103 | first_cap_last_num_re = /^[A-Z].*[0-9]$/ | |
104 | first_cap_last_symbol = 0 | |
105 | first_cap_last_symbol_re = /^[A-Z].*[\p{Punct}]$/ | |
106 | ||
107 | years = {} | |
108 | ||
109 | 1975.upto(2020) do |year| | |
110 | years[year] = 0 | |
111 | end | |
112 | ||
113 | # this is the count of words with 1, 2 and 3 numbers on the end | |
114 | #strict_last_on_end = [0,0,0,0,0] | |
115 | singles_on_end_re = /[^0-9]+([0-9]{1})$/ | |
116 | singles_on_end = 0 | |
117 | doubles_on_end_re = /[^0-9]+([0-9]{2})$/ | |
118 | doubles_on_end = 0 | |
119 | triples_on_end_re =/[^0-9]+([0-9]{3})$/ | |
120 | triples_on_end = 0 | |
121 | ||
122 | # this is the actual last number on the end, single digit | |
123 | last_on_end = [] | |
124 | 0.upto(4) do |no_of_digits| | |
125 | last_on_end[no_of_digits] = {} | |
126 | end | |
127 | ||
128 | # last two and three numbers on the end | |
129 | last_two_on_end = {} | |
130 | last_three_on_end = {} | |
131 | ||
132 | def lookup_by_zipcode(zip, key) | |
133 | geocoder = "http://maps.google.com/maps/geo?q=" | |
134 | apikey = "&key=" + key | |
135 | ||
136 | # Since the zipcode goes directly into the URL request it needs to be cleaned up. | |
137 | request = geocoder + URI::encode(zip + ', USA') + apikey | |
138 | resp = Net::HTTP.get_response(URI.parse(request)) | |
139 | ||
140 | data = JSON.parse(resp.body) | |
141 | ||
142 | if data.has_key?('Status') | |
143 | # puts "status: " + data['Status']['code'].to_s | |
144 | ||
145 | if (data.has_key?('Placemark') and data['Placemark'].length > 0) | |
146 | #puts data['Placemark'][0].inspect | |
147 | # puts "location: " + data['Placemark'][0]['address'] | |
148 | ||
149 | if data['Placemark'][0].has_key? 'AddressDetails' and | |
150 | data['Placemark'][0]['AddressDetails'].has_key? 'Country' and | |
151 | data['Placemark'][0]['AddressDetails']['Country'].has_key? 'AdministrativeArea' and | |
152 | data['Placemark'][0]['AddressDetails']['Country']['AdministrativeArea'].has_key? 'Locality' and | |
153 | data['Placemark'][0]['AddressDetails']['Country']['AdministrativeArea']['Locality'].has_key? 'PostalCode' and | |
154 | data['Placemark'][0]['AddressDetails']['Country']['AdministrativeArea']['Locality']['PostalCode'].has_key? 'PostalCodeNumber' and | |
155 | data['Placemark'][0]['AddressDetails']['Country'].has_key? 'CountryName' | |
156 | ||
157 | # puts "Location: " + data['Placemark'][0]['AddressDetails']['Country']['CountryName'] | |
158 | # puts "Location: " + data['Placemark'][0]['AddressDetails']['Country']['AdministrativeArea']['Locality']['PostalCode']['PostalCodeNumber'] | |
159 | if data['Placemark'][0]['AddressDetails']['Country']['CountryName'] == 'USA' and data['Placemark'][0]['AddressDetails']['Country']['AdministrativeArea']['Locality']['PostalCode']['PostalCodeNumber'] == zip | |
160 | return data['Placemark'][0]['address'] | |
161 | end | |
162 | end | |
163 | ||
164 | # return data['Placemark'][0]['address'] | |
165 | end | |
166 | end | |
167 | ||
168 | return nil | |
169 | end | |
170 | ||
171 | class String | |
172 | def is_numeric? | |
173 | Integer self rescue false | |
174 | end | |
175 | end | |
176 | ||
177 | def is_numeric val | |
178 | return val.to_s =~ /^[0-9]+$/ | |
179 | end | |
180 | ||
181 | opts = GetoptLong.new( | |
182 | [ '--help', '-h', "-?", GetoptLong::NO_ARGUMENT ], | |
183 | [ '--top', "-t" , GetoptLong::REQUIRED_ARGUMENT ], | |
184 | [ '--output', "-o" , GetoptLong::REQUIRED_ARGUMENT ], | |
185 | [ '--external', "-e" , GetoptLong::REQUIRED_ARGUMENT ], | |
186 | [ '--gkey', GetoptLong::REQUIRED_ARGUMENT ], | |
187 | [ "-v" , GetoptLong::NO_ARGUMENT ] | |
188 | ) | |
189 | ||
190 | # Display the usage | |
191 | def usage | |
192 | puts"pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
193 | ||
194 | Usage: pipal [OPTION] ... FILENAME | |
195 | --help, -h: show help | |
196 | --top, -t X: show the top X results (default 10) | |
197 | --output, -o <filename>: output to file | |
198 | --external, -e <filename>: external file to compare words against | |
199 | --gkey <Google Maps API key>: to allow zip code lookups (optional) | |
200 | ||
201 | FILENAME: The file to count | |
202 | ||
203 | " | |
204 | exit | |
205 | end | |
206 | ||
207 | cap_at = 10 | |
208 | output_file = STDOUT | |
209 | external_list = {} | |
210 | google_maps_api_key = GOOGLE_API_KEY | |
211 | ||
212 | begin | |
213 | opts.each do |opt, arg| | |
214 | case opt | |
215 | when '--help' | |
216 | usage | |
217 | when "--top" | |
218 | if arg.is_numeric? | |
219 | cap_at = arg.to_i | |
220 | if cap_at <= 0 | |
221 | puts"pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
222 | ||
223 | Please enter a positive number of lines | |
224 | ||
225 | " | |
226 | exit 1 | |
227 | end | |
228 | else | |
229 | puts"pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
230 | ||
231 | Invalid number of lines | |
232 | ||
233 | " | |
234 | exit 1 | |
235 | end | |
236 | when "--gkey" | |
237 | google_maps_api_key = arg | |
238 | when "--external" | |
239 | if File.exist?(arg) | |
240 | begin | |
241 | File.open(arg, 'r').each_line do |word| | |
242 | external_list[word.force_encoding("ASCII-8BIT").strip] = 0 unless word.force_encoding("ASCII-8BIT").strip == '' | |
243 | end | |
244 | rescue Errno::EACCES => e | |
245 | puts"pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
246 | ||
247 | Unable to open external file | |
248 | ||
249 | " | |
250 | exit 1 | |
251 | end | |
252 | else | |
253 | puts"pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
254 | ||
255 | Unable to find external file | |
256 | ||
257 | " | |
258 | exit 1 | |
259 | end | |
260 | when "--output" | |
261 | begin | |
262 | output_file = File.new(arg, "w") | |
263 | rescue Errno::EACCES => e | |
264 | puts"pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
265 | ||
266 | Unable to open output file | |
267 | ||
268 | " | |
269 | exit 1 | |
270 | end | |
271 | end | |
272 | end | |
273 | rescue GetoptLong::InvalidOption => e | |
274 | puts | |
275 | usage | |
276 | exit | |
277 | rescue => e | |
278 | puts "Something went wrong, please report it to [email protected] along with these messages:" | |
279 | puts | |
280 | puts e.message | |
281 | puts | |
282 | puts e.class.to_s | |
283 | puts | |
284 | puts "Backtrace:" | |
285 | puts e.backtrace | |
286 | puts | |
287 | usage | |
288 | exit 1 | |
289 | end | |
290 | ||
291 | if ARGV.length != 1 | |
292 | puts"pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
293 | ||
294 | Please specify the file to count | |
295 | ||
296 | " | |
297 | exit 1 | |
298 | end | |
299 | ||
300 | filename = ARGV.shift | |
301 | ||
302 | if !File.exist? filename | |
303 | puts"pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
304 | ||
305 | Can't find the password file | |
306 | ||
307 | " | |
308 | exit 2 | |
309 | end | |
310 | ||
311 | lengths = [] | |
312 | max_length = 0 | |
313 | base_words = {} | |
314 | ||
315 | puts "Generating stats, hit CTRL-C to finish early and dump stats on words already processed." | |
316 | puts "Please wait..." | |
317 | ||
318 | if %x{wc -l '#{filename}'}.match(/\s*([0-9]+).*/) | |
319 | file_line_count = $1.to_i | |
320 | else | |
321 | filesize = File.stat(filename).size | |
322 | file_line_count = (filesize / 8).to_i | |
323 | puts "Can't find wc to calculate the number of lines so guessing as " + file_line_count.to_s + " based on file size" | |
324 | end | |
325 | ||
326 | pbar = ProgressBar.new("Processing", file_line_count) | |
327 | ||
328 | catch :ctrl_c do | |
329 | begin | |
330 | File.open(filename, "r").each_line do |line| | |
331 | begin | |
332 | line.strip! | |
333 | if line == "" | |
334 | pbar.inc | |
335 | next | |
336 | end | |
337 | # Doing this so that I can support a wider range of characters, a UK pound sign | |
338 | # breaks the app without it | |
339 | line.force_encoding("ASCII-8BIT") | |
340 | lower_line = line.downcase | |
341 | ||
342 | if !words.has_key?(line) | |
343 | words[line] = 0 | |
344 | end | |
345 | words[line] += 1 | |
346 | ||
347 | # strip any non-alpha from the start or end, I was going to strip all non-alpha | |
348 | # but then found a list with Unc0rn as a very common base. Stripping all non-alpha | |
349 | # would leave with Uncrn which doesn't really make any sense as without the 133t speak | |
350 | # it is out of context. | |
351 | # | |
352 | # If you want all non-alpha stripped use the following line instead | |
353 | # | |
354 | # word_just_alpha = lower_line.gsub(/[^a-z]*/, "") | |
355 | # | |
356 | word_just_alpha = lower_line.gsub(/^[^a-z]*/, "").gsub(/[^a-z]*$/, '') | |
357 | if word_just_alpha.length > 3 | |
358 | if !base_words.has_key?(word_just_alpha) | |
359 | base_words[word_just_alpha] = 0 | |
360 | end | |
361 | base_words[word_just_alpha] += 1 | |
362 | end | |
363 | ||
364 | if lengths[line.length].nil? | |
365 | lengths[line.length] = 0 | |
366 | end | |
367 | lengths[line.length] += 1 | |
368 | ||
369 | # if line.length > max_length | |
370 | # max_length = line.length | |
371 | # end | |
372 | ||
373 | if line.length < 9 | |
374 | one_to_eight_chars += 1 | |
375 | end | |
376 | ||
377 | if line.length < 7 | |
378 | one_to_six_chars += 1 | |
379 | end | |
380 | ||
381 | if line.length > 8 | |
382 | over_eight_chars += 1 | |
383 | end | |
384 | ||
385 | # Replaced with character set upperalpha | |
386 | # if line. =~ /^[A-Z]*$/ | |
387 | # only_upper_alpha_chars += 1 | |
388 | # end | |
389 | ||
390 | # Replaced with character set loweralpha | |
391 | # if line. =~ /^[a-z]*$/ | |
392 | # only_lower_alpha_chars += 1 | |
393 | # end | |
394 | ||
395 | # Replaced with character set alldigit | |
396 | # if line. =~ /^[0-9]*$/ | |
397 | # only_numeric_chars += 1 | |
398 | # end | |
399 | ||
400 | if line =~ first_cap_last_symbol_re | |
401 | first_cap_last_symbol += 1 | |
402 | end | |
403 | ||
404 | if line =~ first_cap_last_num_re | |
405 | first_cap_last_num += 1 | |
406 | end | |
407 | ||
408 | years.each_pair do |year, count| | |
409 | if /#{year}/.match line | |
410 | years[year] += 1 | |
411 | end | |
412 | end | |
413 | ||
414 | days_ab.each_pair do |day, count| | |
415 | if /#{day}/i.match line | |
416 | days_ab[day] += 1 | |
417 | end | |
418 | end | |
419 | ||
420 | months_ab.each_pair do |month, count| | |
421 | if /#{month}/i.match line | |
422 | months_ab[month] += 1 | |
423 | end | |
424 | end | |
425 | ||
426 | colours.each_pair do |colour, count| | |
427 | if /#{colour}/i.match line | |
428 | colours[colour] += 1 | |
429 | end | |
430 | end | |
431 | ||
432 | days.each_pair do |day, count| | |
433 | if /#{day}/i.match line | |
434 | days[day] += 1 | |
435 | end | |
436 | end | |
437 | ||
438 | months.each_pair do |month, count| | |
439 | if /#{month}/i.match line | |
440 | months[month] += 1 | |
441 | end | |
442 | end | |
443 | ||
444 | external_list.each_pair do |domain, count| | |
445 | if /#{Regexp.quote(domain)}/i.match line | |
446 | external_list[domain] += 1 | |
447 | end | |
448 | end | |
449 | ||
450 | #if line =~ /[^0-9]+([0-9]{1})$/ | |
451 | # if /[^0-9]+([0-9]{1})$/.match line | |
452 | if singles_on_end_re.match line | |
453 | singles_on_end += 1 | |
454 | end | |
455 | ||
456 | # Can't merge these two as the first is strict, 2 digits on the end, the second | |
457 | # just wants the last two digits regardless | |
458 | # if /[^0-9]+([0-9]{2})$/.match line | |
459 | if doubles_on_end_re.match line | |
460 | # if line =~ /[^0-9]+([0-9]{2})$/ | |
461 | doubles_on_end += 1 | |
462 | end | |
463 | ||
464 | # if /[^0-9]+([0-9]{3})$/.match line | |
465 | if triples_on_end_re.match line | |
466 | #if line =~ /[^0-9]+([0-9]{3})$/ | |
467 | triples_on_end += 1 | |
468 | end | |
469 | ||
470 | 1.upto(5) do |no_of_digits| | |
471 | if /([0-9]{#{no_of_digits}})$/.match line | |
472 | last_numbers = $1 | |
473 | if !last_on_end[no_of_digits - 1].has_key?(last_numbers) | |
474 | last_on_end[no_of_digits - 1][last_numbers] = 0 | |
475 | end | |
476 | last_on_end[no_of_digits - 1][last_numbers] += 1 | |
477 | end | |
478 | # if /[^0-9]+([0-9]{#{no_of_digits}})$/.match line | |
479 | # strict_last_on_end[no_of_digits - 1] += 1 | |
480 | # end | |
481 | ||
482 | end | |
483 | ||
484 | # numbers_on_end.each_pair do |number, count| | |
485 | # if /[^0-9]*#{number}$/.match line | |
486 | # numbers_on_end[number] += 1 | |
487 | # end | |
488 | # end | |
489 | ||
490 | char_stats.each_pair do |name, data| | |
491 | begin | |
492 | if line =~ data['regex'] | |
493 | char_stats[name]['count'] += 1 | |
494 | break | |
495 | end | |
496 | rescue Encoding::CompatibilityError | |
497 | puts "Encoding problem found with password: " + line | |
498 | end | |
499 | end | |
500 | ||
501 | char_sets_ordering.each_pair do |name, data| | |
502 | begin | |
503 | if lower_line =~ data['regex'] | |
504 | char_sets_ordering[name]['count'] += 1 | |
505 | break | |
506 | end | |
507 | rescue Encoding::CompatibilityError | |
508 | puts "Encoding problem found with password: " + line | |
509 | end | |
510 | end | |
511 | ||
512 | # This won't work as the special replacement hits all the previous ?'s that have been replaced, | |
513 | # lower at the end would do the same with all the characters so can't use the order to fix this problem | |
514 | # mask_line = line.gsub(/[a-z]/, "?l").gsub(/[A-Z]/,'?u').gsub(/[0-9]/, '?d').gsub(/[\p{Punct}]/, '?s') | |
515 | mask_line = "" | |
516 | line.each_char do |char| | |
517 | case char | |
518 | when /[a-z]/ | |
519 | mask_line << "?l" | |
520 | when /[A-Z]/ | |
521 | mask_line << "?u" | |
522 | when /[0-9]/ | |
523 | mask_line << "?d" | |
524 | else | |
525 | mask_line << "?s" | |
526 | end | |
527 | end | |
528 | ||
529 | if !hashcat_masks.has_key? mask_line | |
530 | hashcat_masks[mask_line] = {'count' => 0} | |
531 | end | |
532 | hashcat_masks[mask_line]['count'] += 1 | |
533 | ||
534 | pbar.inc | |
535 | ||
536 | total_lines += 1 | |
537 | rescue ArgumentError => e | |
538 | puts "Encoding problem processing word: " + line | |
539 | pbar.inc | |
540 | rescue => e | |
541 | puts "Something went wrong, please report it to [email protected] along with these messages:" | |
542 | puts | |
543 | puts e.message | |
544 | puts | |
545 | puts e.class.to_s | |
546 | puts | |
547 | puts "Backtrace:" | |
548 | puts e.backtrace | |
549 | puts | |
550 | usage | |
551 | exit 1 | |
552 | end | |
553 | end | |
554 | rescue Errno::EACCES => e | |
555 | puts"pipal 2.0 Robin Wood ([email protected]) (www.digininja.org) | |
556 | ||
557 | Unable to open the password file | |
558 | ||
559 | " | |
560 | exit 1 | |
561 | rescue => e | |
562 | puts "Something went wrong, please report it to [email protected] along with these messages:" | |
563 | puts | |
564 | puts e.message | |
565 | puts | |
566 | puts e.class.to_s | |
567 | puts | |
568 | puts "Backtrace:" | |
569 | puts e.backtrace | |
570 | puts | |
571 | usage | |
572 | exit 1 | |
573 | end | |
574 | end | |
575 | ||
576 | pbar.halt | |
577 | ||
578 | # This is a screen puts to clear after the status bars in case the data is being written to the screen, do not add outfile to it | |
579 | puts | |
580 | puts | |
581 | ||
582 | output_file.puts "Total entries = " + total_lines.to_s | |
583 | uniq_words = words.to_a.uniq | |
584 | output_file.puts "Total unique entries = " + uniq_words.length.to_s | |
585 | uniq_words = Array.new(words.to_a.uniq) | |
586 | ||
587 | output_file.puts | |
588 | output_file.puts "Top " + cap_at.to_s + " passwords" | |
589 | # The default is to sort lowest to highest, the -1 just inverts that | |
590 | words.sort{|a,b| (a[1]<=>b[1]) * -1}[0, cap_at].each { |elem| | |
591 | percentage = (elem[1].to_f / total_lines) * 100 | |
592 | output_file.puts elem[0] + " = " + elem[1].to_s + " (" + percentage.round(2).to_s + "%)" | |
593 | } | |
594 | ||
595 | output_file.puts | |
596 | output_file.puts "Top " + cap_at.to_s + " base words" | |
597 | base_words.sort{|a,b| (a[1]<=>b[1]) * -1}[0, cap_at].each { |elem| | |
598 | percentage = (elem[1].to_f / total_lines) * 100 | |
599 | output_file.puts elem[0] + " = " + elem[1].to_s + " (" + percentage.round(2).to_s + "%)" | |
600 | } | |
601 | ||
602 | output_file.puts | |
603 | output_file.puts "Password length (length ordered)" | |
604 | ||
605 | length_ordered = [] | |
606 | 0.upto(lengths.count) do |len| | |
607 | if lengths[len].nil? | |
608 | lengths[len] = 0 | |
609 | end | |
610 | percentage = ((lengths[len].to_f / total_lines) * 100) | |
611 | output_file.puts len.to_s + ' = ' + lengths[len].to_s + " (" + percentage.round(2).to_s + "%)" if lengths[len] > 0 | |
612 | ||
613 | pair = [len, lengths[len], percentage] | |
614 | length_ordered << pair | |
615 | end | |
616 | ||
617 | length_ordered.sort! do |x,y| | |
618 | y[1] <=> x[1] | |
619 | end | |
620 | ||
621 | output_file.puts | |
622 | output_file.puts "Password length (count ordered)" | |
623 | length_ordered.each do |pair| | |
624 | output_file.puts pair[0].to_s + " = " + pair[1].to_s + " (" + pair[2].round(2).to_s + "%)" if pair[1] > 0 | |
625 | end | |
626 | ||
627 | output_file.puts | |
628 | ||
629 | horiz = HorizBar.new(lengths) | |
630 | horiz.output_file = output_file | |
631 | horiz.draw | |
632 | ||
633 | output_file.puts "One to six characters = " + one_to_six_chars.to_s + ' (' + ((one_to_six_chars.to_f/total_lines) * 100).round(2).to_s + '%)' | |
634 | output_file.puts "One to eight characters = " + one_to_eight_chars.to_s + ' (' + ((one_to_eight_chars.to_f/total_lines) * 100).round(2).to_s + '%)' | |
635 | output_file.puts "More than eight characters = " + over_eight_chars.to_s + ' (' + ((over_eight_chars.to_f/total_lines) * 100).round(2).to_s + '%)' | |
636 | ||
637 | output_file.puts | |
638 | ||
639 | output_file.puts "Only lowercase alpha = " + char_stats['loweralpha']['count'].to_s + ' (' + ((char_stats['loweralpha']['count'].to_f/total_lines) * 100).round(2).to_s + '%)' | |
640 | output_file.puts "Only uppercase alpha = " + char_stats['upperalpha']['count'].to_s + ' (' + ((char_stats['upperalpha']['count'].to_f/total_lines) * 100).round(2).to_s + '%)' | |
641 | output_file.puts "Only alpha = " + (char_stats['upperalpha']['count'] + char_stats['loweralpha']['count']).to_s + ' (' + (((char_stats['upperalpha']['count'] + char_stats['loweralpha']['count']).to_f/total_lines) * 100).round(2).to_s + '%)' | |
642 | ||
643 | output_file.puts "Only numeric = " + char_stats['numeric']['count'].to_s + ' (' + ((char_stats['numeric']['count'].to_f/total_lines) * 100).round(2).to_s + '%)' | |
644 | ||
645 | output_file.puts | |
646 | output_file.puts "First capital last symbol = " + first_cap_last_symbol.to_s + ' (' + ((first_cap_last_symbol.to_f/total_lines) * 100).round(2).to_s + '%)' | |
647 | output_file.puts "First capital last number = " + first_cap_last_num.to_s + ' (' + ((first_cap_last_num.to_f/total_lines) * 100).round(2).to_s + '%)' | |
648 | ||
649 | if external_list.length > 0 | |
650 | count_ordered = [] | |
651 | external_list.each_pair do |domain, count| | |
652 | count_ordered << [domain, count] unless count == 0 | |
653 | end | |
654 | external_list = count_ordered.sort do |x,y| | |
655 | (x[1] <=> y[1]) * -1 | |
656 | end | |
657 | ||
658 | output_file.puts | |
659 | output_file.puts "External list (Top " + cap_at.to_s + ")" | |
660 | disp = false | |
661 | external_list[0, cap_at].each do |data| | |
662 | disp = true | |
663 | output_file.puts data[0] + " = " + data[1].to_s + ' (' + ((data[1].to_f/total_lines) * 100).round(2).to_s + '%)' | |
664 | end | |
665 | unless disp | |
666 | output_file.puts "None found" | |
667 | end | |
668 | end | |
669 | ||
670 | output_file.puts | |
671 | output_file.puts "Months" | |
672 | disp = false | |
673 | months.each_pair do |month, count| | |
674 | unless count == 0 | |
675 | disp = true | |
676 | output_file.puts month + " = " + count.to_s + ' (' + ((count.to_f/total_lines) * 100).round(2).to_s + '%)'unless count == 0 | |
677 | end | |
678 | end | |
679 | unless disp | |
680 | output_file.puts "None found" | |
681 | end | |
682 | ||
683 | output_file.puts | |
684 | output_file.puts "Days" | |
685 | disp = false | |
686 | days.each_pair do |day, count| | |
687 | unless count == 0 | |
688 | disp = true | |
689 | output_file.puts day + " = " + count.to_s + ' (' + ((count.to_f/total_lines) * 100).round(2).to_s + '%)' unless count == 0 | |
690 | end | |
691 | end | |
692 | unless disp | |
693 | output_file.puts "None found" | |
694 | end | |
695 | ||
696 | output_file.puts | |
697 | output_file.puts "Months (Abreviated)" | |
698 | disp = false | |
699 | months_ab.each_pair do |month, count| | |
700 | unless count == 0 | |
701 | disp = true | |
702 | output_file.puts month + " = " + count.to_s + ' (' + ((count.to_f/total_lines) * 100).round(2).to_s + '%)' unless count == 0 | |
703 | end | |
704 | end | |
705 | unless disp | |
706 | output_file.puts "None found" | |
707 | end | |
708 | ||
709 | output_file.puts | |
710 | output_file.puts "Days (Abreviated)" | |
711 | disp = false | |
712 | days_ab.each_pair do |day, count| | |
713 | unless count == 0 | |
714 | disp = true | |
715 | output_file.puts day + " = " + count.to_s + ' (' + ((count.to_f/total_lines) * 100).round(2).to_s + '%)' unless count == 0 | |
716 | end | |
717 | end | |
718 | unless disp | |
719 | output_file.puts "None found" | |
720 | end | |
721 | ||
722 | output_file.puts | |
723 | output_file.puts "Includes years" | |
724 | disp = false | |
725 | years.each_pair do |number, count| | |
726 | unless count == 0 | |
727 | disp = true | |
728 | output_file.puts number.to_s + " = " + count.to_s + ' (' + ((count.to_f/total_lines) * 100).round(2).to_s + '%)'unless count == 0 | |
729 | end | |
730 | end | |
731 | unless disp | |
732 | output_file.puts "None found" | |
733 | end | |
734 | ||
735 | count_ordered = [] | |
736 | years.each_pair do |year, count| | |
737 | count_ordered << [year, count] unless count == 0 | |
738 | end | |
739 | years = count_ordered.sort do |x,y| | |
740 | (x[1] <=> y[1]) * -1 | |
741 | end | |
742 | ||
743 | output_file.puts | |
744 | output_file.puts "Years (Top " + cap_at.to_s + ")" | |
745 | disp = false | |
746 | years[0, cap_at].each do |data| | |
747 | disp = true | |
748 | output_file.puts data[0].to_s + " = " + data[1].to_s + ' (' + ((data[1].to_f/total_lines) * 100).round(2).to_s + '%)' | |
749 | end | |
750 | unless disp | |
751 | output_file.puts "None found" | |
752 | end | |
753 | ||
754 | output_file.puts | |
755 | output_file.puts "Colours" | |
756 | disp = false | |
757 | colours.each_pair do |colour, count| | |
758 | unless count == 0 | |
759 | disp = true | |
760 | output_file.puts colour + " = " + count.to_s + ' (' + ((count.to_f/total_lines) * 100).round(2).to_s + '%)' unless count == 0 | |
761 | end | |
762 | end | |
763 | unless disp | |
764 | output_file.puts "None found" | |
765 | end | |
766 | ||
767 | ||
768 | output_file.puts | |
769 | ||
770 | #output_file.puts "Single digit on the end = " + strict_last_on_end[2].count.to_s + ' (' + ((strict_last_on_end[0].count.to_f/total_lines) * 100).round(2).to_s + '%)' | |
771 | #output_file.puts "Two digits on the end = " + strict_last_on_end[2].count.to_s + ' (' + ((strict_last_on_end[1].count.to_f/total_lines) * 100).round(2).to_s + '%)' | |
772 | #output_file.puts "Three digits on the end = " + strict_last_on_end[2].count.to_s + ' (' + ((strict_last_on_end[2].count.to_f/total_lines) * 100).round(2).to_s + '%)' | |
773 | ||
774 | output_file.puts "Single digit on the end = " + singles_on_end.to_s + ' (' + ((singles_on_end.to_f/total_lines) * 100).round(2).to_s + '%)' | |
775 | output_file.puts "Two digits on the end = " + doubles_on_end.to_s + ' (' + ((doubles_on_end.to_f/total_lines) * 100).round(2).to_s + '%)' | |
776 | output_file.puts "Three digits on the end = " + triples_on_end.to_s + ' (' + ((triples_on_end.to_f/total_lines) * 100).round(2).to_s + '%)' | |
777 | ||
778 | output_file.puts | |
779 | output_file.puts "Last number" | |
780 | disp = false | |
781 | ||
782 | graph_numbers = {0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0, 7=>0, 8=>0, 9=>0} | |
783 | ||
784 | c = last_on_end[0].to_a.sort do |x,y| | |
785 | (x[0] <=> y[0]) | |
786 | end | |
787 | ||
788 | c.each do |number, count| | |
789 | unless count == 0 | |
790 | disp = true | |
791 | output_file.puts number.to_s + " = " + count.to_s + ' (' + ((count.to_f/total_lines) * 100).round(2).to_s + '%)' unless count == 0 | |
792 | end | |
793 | graph_numbers[number.to_i] = count | |
794 | end | |
795 | unless disp | |
796 | output_file.puts "None found" | |
797 | end | |
798 | ||
799 | output_file.puts | |
800 | horiz = HorizBar.new(graph_numbers.values) | |
801 | horiz.output_file = output_file | |
802 | horiz.draw | |
803 | ||
804 | area_codes = [] | |
805 | zip_codes = [] | |
806 | ||
807 | digit_number = 0 | |
808 | last_on_end.each do |a| | |
809 | c = a.to_a.sort do |x,y| | |
810 | (x[1] <=> y[1]) * -1 | |
811 | end | |
812 | ||
813 | digit_number += 1 | |
814 | if c.count > 0 | |
815 | if (digit_number == 1) | |
816 | output_file.puts "Last digit" | |
817 | else | |
818 | output_file.puts "Last " + digit_number.to_s + " digits (Top " + cap_at.to_s + ")" | |
819 | end | |
820 | ||
821 | c[0, cap_at].each do |d| | |
822 | output_file.puts d[0] + " = " + d[1].to_s + ' (' + ((d[1].to_f/total_lines) * 100).round(2).to_s + '%)' | |
823 | ||
824 | if digit_number == 3 | |
825 | area_codes << d[0] | |
826 | end | |
827 | if digit_number == 5 | |
828 | zip_codes << d[0] | |
829 | end | |
830 | end | |
831 | output_file.puts | |
832 | end | |
833 | end | |
834 | ||
835 | count_ordered = [] | |
836 | char_stats.each_pair do |name, data| | |
837 | count_ordered << [name, data] unless data['count'] == 0 | |
838 | end | |
839 | char_stats = count_ordered.sort do |x,y| | |
840 | (x[1]['count'] <=> y[1]['count']) * -1 | |
841 | end | |
842 | ||
843 | areas = {} | |
844 | area_codes.each do |code| | |
845 | code = code.to_s | |
846 | if US_area_codes.has_key? code | |
847 | area = US_area_codes[code] | |
848 | areas[code] = area | |
849 | end | |
850 | end | |
851 | if areas.length > 0 | |
852 | output_file.puts "US Area Codes" | |
853 | areas.each_pair do |code, area| | |
854 | output_file.puts code + ' = ' + area[1] + " (" + area[0] + ")" | |
855 | end | |
856 | output_file.puts | |
857 | end | |
858 | ||
859 | if google_maps_api_key != "" | |
860 | areas = {} | |
861 | zip_codes.each do |zip| | |
862 | area = lookup_by_zipcode zip.to_s, google_maps_api_key | |
863 | unless area.nil? | |
864 | areas[zip] = area | |
865 | end | |
866 | end | |
867 | if areas.length > 0 | |
868 | output_file.puts "US Zip Codes" | |
869 | areas.each_pair do |zip, area| | |
870 | output_file.puts zip + ' = ' + area | |
871 | end | |
872 | output_file.puts | |
873 | end | |
874 | end | |
875 | ||
876 | output_file.puts "Character sets" | |
877 | char_stats.each do |name, data| | |
878 | output_file.puts name + ": " + data['count'].to_s + " (" + ((data['count'].to_f/total_lines) * 100).round(2).to_s + "%)" | |
879 | end | |
880 | ||
881 | count_ordered = [] | |
882 | char_sets_ordering.each_pair do |name, data| | |
883 | count_ordered << [name, data] unless data['count'] == 0 | |
884 | end | |
885 | char_sets_ordering = count_ordered.sort do |x,y| | |
886 | (x[1]['count'] <=> y[1]['count']) * -1 | |
887 | end | |
888 | ||
889 | output_file.puts | |
890 | output_file.puts "Character set ordering" | |
891 | char_sets_ordering.each do |name, data| | |
892 | output_file.puts name + ": " + data['count'].to_s + " (" + ((data['count'].to_f/total_lines) * 100).round(2).to_s + "%)" | |
893 | end | |
894 | ||
895 | count_ordered = [] | |
896 | hashcat_masks.each_pair do |name, data| | |
897 | count_ordered << [name, data] unless data['count'] == 0 | |
898 | end | |
899 | hashcat_masks = count_ordered.sort do |x,y| | |
900 | (x[1]['count'] <=> y[1]['count']) * -1 | |
901 | end | |
902 | ||
903 | output_file.puts | |
904 | output_file.puts "Hashcat masks (Top " + cap_at.to_s + ")" | |
905 | hashcat_masks[0, cap_at].each do |name, data| | |
906 | output_file.puts name + ": " + data['count'].to_s + " (" + ((data['count'].to_f/total_lines) * 100).round(2).to_s + "%)" | |
907 | end | |
908 | ||
909 | end | |
910 | puts time if false |
0 | # | |
1 | # Ruby/ProgressBar - a text progress bar library | |
2 | # | |
3 | # Copyright (C) 2001-2005 Satoru Takabayashi <[email protected]> | |
4 | # All rights reserved. | |
5 | # This is free software with ABSOLUTELY NO WARRANTY. | |
6 | # | |
7 | # You can redistribute it and/or modify it under the terms | |
8 | # of Ruby's license. | |
9 | # | |
10 | ||
11 | class ProgressBar | |
12 | VERSION = "0.9" | |
13 | ||
14 | def initialize (title, total, out = STDERR) | |
15 | @title = title | |
16 | @total = total | |
17 | @out = out | |
18 | @terminal_width = 80 | |
19 | @bar_mark = "o" | |
20 | @current = 0 | |
21 | @previous = 0 | |
22 | @finished_p = false | |
23 | @start_time = Time.now | |
24 | @previous_time = @start_time | |
25 | @title_width = 14 | |
26 | @format = "%-#{@title_width}s %3d%% %s %s" | |
27 | @format_arguments = [:title, :percentage, :bar, :stat] | |
28 | clear | |
29 | show | |
30 | end | |
31 | attr_reader :title | |
32 | attr_reader :current | |
33 | attr_reader :total | |
34 | attr_accessor :start_time | |
35 | ||
36 | private | |
37 | def fmt_bar | |
38 | bar_width = do_percentage * @terminal_width / 100 | |
39 | sprintf("|%s%s|", | |
40 | @bar_mark * bar_width, | |
41 | " " * (@terminal_width - bar_width)) | |
42 | end | |
43 | ||
44 | def fmt_percentage | |
45 | do_percentage | |
46 | end | |
47 | ||
48 | def fmt_stat | |
49 | if @finished_p then elapsed else eta end | |
50 | end | |
51 | ||
52 | def fmt_stat_for_file_transfer | |
53 | if @finished_p then | |
54 | sprintf("%s %s %s", bytes, transfer_rate, elapsed) | |
55 | else | |
56 | sprintf("%s %s %s", bytes, transfer_rate, eta) | |
57 | end | |
58 | end | |
59 | ||
60 | def fmt_title | |
61 | @title[0,(@title_width - 1)] + ":" | |
62 | end | |
63 | ||
64 | def convert_bytes (bytes) | |
65 | if bytes < 1024 | |
66 | sprintf("%6dB", bytes) | |
67 | elsif bytes < 1024 * 1000 # 1000kb | |
68 | sprintf("%5.1fKB", bytes.to_f / 1024) | |
69 | elsif bytes < 1024 * 1024 * 1000 # 1000mb | |
70 | sprintf("%5.1fMB", bytes.to_f / 1024 / 1024) | |
71 | else | |
72 | sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024) | |
73 | end | |
74 | end | |
75 | ||
76 | def transfer_rate | |
77 | bytes_per_second = @current.to_f / (Time.now - @start_time) | |
78 | sprintf("%s/s", convert_bytes(bytes_per_second)) | |
79 | end | |
80 | ||
81 | def bytes | |
82 | convert_bytes(@current) | |
83 | end | |
84 | ||
85 | def format_time (t) | |
86 | t = t.to_i | |
87 | sec = t % 60 | |
88 | min = (t / 60) % 60 | |
89 | hour = t / 3600 | |
90 | sprintf("%02d:%02d:%02d", hour, min, sec); | |
91 | end | |
92 | ||
93 | # ETA stands for Estimated Time of Arrival. | |
94 | def eta | |
95 | if @current == 0 | |
96 | "ETA: --:--:--" | |
97 | else | |
98 | elapsed = Time.now - @start_time | |
99 | eta = elapsed * @total / @current - elapsed; | |
100 | sprintf("ETA: %s", format_time(eta)) | |
101 | end | |
102 | end | |
103 | ||
104 | def elapsed | |
105 | elapsed = Time.now - @start_time | |
106 | sprintf("Time: %s", format_time(elapsed)) | |
107 | end | |
108 | ||
109 | def eol | |
110 | if @finished_p then "\n" else "\r" end | |
111 | end | |
112 | ||
113 | def do_percentage | |
114 | if @total.zero? | |
115 | 100 | |
116 | else | |
117 | @current * 100 / @total | |
118 | end | |
119 | end | |
120 | ||
121 | def get_width | |
122 | # FIXME: I don't know how portable it is. | |
123 | default_width = 80 | |
124 | begin | |
125 | tiocgwinsz = 0x5413 | |
126 | data = [0, 0, 0, 0].pack("SSSS") | |
127 | if @out.ioctl(tiocgwinsz, data) >= 0 then | |
128 | rows, cols, xpixels, ypixels = data.unpack("SSSS") | |
129 | if cols >= 0 then cols else default_width end | |
130 | else | |
131 | default_width | |
132 | end | |
133 | rescue Exception | |
134 | default_width | |
135 | end | |
136 | end | |
137 | ||
138 | def show | |
139 | arguments = @format_arguments.map {|method| | |
140 | method = sprintf("fmt_%s", method) | |
141 | send(method) | |
142 | } | |
143 | line = sprintf(@format, *arguments) | |
144 | ||
145 | width = get_width | |
146 | if line.length == width - 1 | |
147 | @out.print(line + eol) | |
148 | @out.flush | |
149 | elsif line.length >= width | |
150 | @terminal_width = [@terminal_width - (line.length - width + 1), 0].max | |
151 | if @terminal_width == 0 then @out.print(line + eol) else show end | |
152 | else # line.length < width - 1 | |
153 | @terminal_width += width - line.length + 1 | |
154 | show | |
155 | end | |
156 | @previous_time = Time.now | |
157 | end | |
158 | ||
159 | def show_if_needed | |
160 | if @total.zero? | |
161 | cur_percentage = 100 | |
162 | prev_percentage = 0 | |
163 | else | |
164 | cur_percentage = (@current * 100 / @total).to_i | |
165 | prev_percentage = (@previous * 100 / @total).to_i | |
166 | end | |
167 | ||
168 | # Use "!=" instead of ">" to support negative changes | |
169 | if cur_percentage != prev_percentage || | |
170 | Time.now - @previous_time >= 1 || @finished_p | |
171 | show | |
172 | end | |
173 | end | |
174 | ||
175 | public | |
176 | def clear | |
177 | @out.print "\r" | |
178 | @out.print(" " * (get_width - 1)) | |
179 | @out.print "\r" | |
180 | end | |
181 | ||
182 | def finish | |
183 | @current = @total | |
184 | @finished_p = true | |
185 | show | |
186 | end | |
187 | ||
188 | def finished? | |
189 | @finished_p | |
190 | end | |
191 | ||
192 | def file_transfer_mode | |
193 | @format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer] | |
194 | end | |
195 | ||
196 | def format= (format) | |
197 | @format = format | |
198 | end | |
199 | ||
200 | def format_arguments= (arguments) | |
201 | @format_arguments = arguments | |
202 | end | |
203 | ||
204 | def halt | |
205 | @finished_p = true | |
206 | show | |
207 | end | |
208 | ||
209 | def inc (step = 1) | |
210 | @current += step | |
211 | @current = @total if @current > @total | |
212 | show_if_needed | |
213 | @previous = @current | |
214 | end | |
215 | ||
216 | def set (count) | |
217 | if count < 0 || count > @total | |
218 | raise "invalid count: #{count} (total: #{@total})" | |
219 | end | |
220 | @current = count | |
221 | show_if_needed | |
222 | @previous = @current | |
223 | end | |
224 | ||
225 | def inspect | |
226 | "#<ProgressBar:#{@current}/#{@total}>" | |
227 | end | |
228 | end | |
229 | ||
230 | class ReversedProgressBar < ProgressBar | |
231 | def do_percentage | |
232 | 100 - super | |
233 | end | |
234 | end | |
235 |
0 | US_area_codes = {} | |
1 | ||
2 | US_area_codes["201"] = ["NJ","N New Jersey: Jersey City, Hackensack"] | |
3 | US_area_codes["202"] = ["DC","Washington, D.C."] | |
4 | US_area_codes["203"] = ["CT","Connecticut: Fairfield County and New Haven County; Bridgeport, New Haven"] | |
5 | US_area_codes["204"] = ["MB","Canada: Manitoba"] | |
6 | US_area_codes["205"] = ["AL","Central Alabama (including Birmingham)"] | |
7 | US_area_codes["206"] = ["WA","W Washington state: Seattle and Bainbridge Island"] | |
8 | US_area_codes["207"] = ["ME","Maine"] | |
9 | US_area_codes["208"] = ["ID","Idaho"] | |
10 | US_area_codes["209"] = ["CA","Cent. California: Stockton"] | |
11 | US_area_codes["210"] = ["TX","S Texas: San Antonio"] | |
12 | US_area_codes["211"] = ["--","Local community info / referral services"] | |
13 | US_area_codes["212"] = ["NY","New York City, New York"] | |
14 | US_area_codes["213"] = ["CA","S California: Los Angeles"] | |
15 | US_area_codes["214"] = ["TX","Texas: Dallas Metro"] | |
16 | US_area_codes["215"] = ["PA","SE Pennsylvania: Philadelphia"] | |
17 | US_area_codes["216"] = ["OH","Cleveland"] | |
18 | US_area_codes["217"] = ["IL","Cent. Illinois: Springfield"] | |
19 | US_area_codes["218"] = ["MN","N Minnesota: Duluth"] | |
20 | US_area_codes["219"] = ["IN","NW Indiana: Gary"] | |
21 | US_area_codes["224"] = ["IL","Northern NE Illinois: Evanston, Waukegan, Northbrook"] | |
22 | US_area_codes["225"] = ["LA","Louisiana: Baton Rouge, New Roads, Donaldsonville, Albany, Gonzales, Greensburg, Plaquemine, Vacherie"] | |
23 | US_area_codes["226"] = ["ON","Canada: SW Ontario: Windsor"] | |
24 | US_area_codes["228"] = ["MS","S Mississippi"] | |
25 | US_area_codes["229"] = ["GA","SW Georgia: Albany"] | |
26 | US_area_codes["231"] = ["MI","W Michigan: Northwestern portion of lower Peninsula; Traverse City, Muskegon, Cheboygan, Alanson"] | |
27 | US_area_codes["234"] = ["OH","NE Ohio: Canton, Akron"] | |
28 | US_area_codes["236"] = ["VA","Virginia (region unknown) / Unassigned?"] | |
29 | US_area_codes["239"] = ["FL","Florida"] | |
30 | US_area_codes["240"] = ["MD","W Maryland: Silver Spring, Frederick, Gaithersburg"] | |
31 | US_area_codes["242"] = ["--","Bahamas"] | |
32 | US_area_codes["246"] = ["--","Barbados"] | |
33 | US_area_codes["248"] = ["MI","Michigan: Oakland County, Pontiac"] | |
34 | US_area_codes["250"] = ["BC","Canada: British Columbia"] | |
35 | US_area_codes["251"] = ["AL","S Alabama: Mobile and coastal areas, Jackson, Evergreen, Monroeville"] | |
36 | US_area_codes["252"] = ["NC","E North Carolina"] | |
37 | US_area_codes["253"] = ["WA","Washington: South Tier - Tacoma, Federal Way"] | |
38 | US_area_codes["254"] = ["TX","Central Texas"] | |
39 | US_area_codes["256"] = ["AL","E and N Alabama"] | |
40 | US_area_codes["260"] = ["IN","NE Indiana: Fort Wayne"] | |
41 | US_area_codes["262"] = ["WI","SE Wisconsin: counties of Kenosha, Ozaukee, Racine, Walworth, Washington, Waukesha"] | |
42 | US_area_codes["264"] = ["--","Anguilla"] | |
43 | US_area_codes["267"] = ["PA","SE Pennsylvania: Philadelphia"] | |
44 | US_area_codes["268"] = ["--","Antigua and Barbuda"] | |
45 | US_area_codes["269"] = ["MI","SW Michigan: Kalamazoo, Saugatuck, Hastings, Battle Creek, Sturgis to Lake Michigan"] | |
46 | US_area_codes["270"] = ["KY","W Kentucky: Bowling Green, Paducah"] | |
47 | US_area_codes["276"] = ["VA","S and SW Virginia: Bristol, Stuart, Martinsville"] | |
48 | US_area_codes["278"] = ["MI","Michigan"] | |
49 | US_area_codes["281"] = ["TX","Texas: Houston Metro"] | |
50 | US_area_codes["283"] = ["OH","SW Ohio: Cincinnati"] | |
51 | US_area_codes["284"] = ["--","British Virgin Islands"] | |
52 | US_area_codes["289"] = ["ON","Canada: S Cent. Ontario: Greater Toronto Area -- Durham, Halton, Hamilton-Wentworth, Niagara, Peel, York, and southern Simcoe County"] | |
53 | US_area_codes["301"] = ["MD","W Maryland: Silver Spring, Frederick, Camp Springs, Prince George's County"] | |
54 | US_area_codes["302"] = ["DE","Delaware"] | |
55 | US_area_codes["303"] = ["CO","Central Colorado: Denver"] | |
56 | US_area_codes["304"] = ["WV","West Virginia"] | |
57 | US_area_codes["305"] = ["FL","SE Florida: Miami, the Keys"] | |
58 | US_area_codes["306"] = ["SK","Canada: Saskatchewan"] | |
59 | US_area_codes["307"] = ["WY","Wyoming"] | |
60 | US_area_codes["308"] = ["NE","W Nebraska: North Platte"] | |
61 | US_area_codes["309"] = ["IL","W Cent. Illinois: Peoria"] | |
62 | US_area_codes["310"] = ["CA","S California: Beverly Hills, West Hollywood, West Los Angeles"] | |
63 | US_area_codes["311"] = ["--","Reserved for special applications"] | |
64 | US_area_codes["312"] = ["IL","Illinois: Chicago"] | |
65 | US_area_codes["313"] = ["MI","Michigan: Detroit and suburbs"] | |
66 | US_area_codes["314"] = ["MO","SE Missouri: St Louis city and parts of the metro area only"] | |
67 | US_area_codes["315"] = ["NY","N Cent. New York: Syracuse"] | |
68 | US_area_codes["316"] = ["KS","S Kansas: Wichita"] | |
69 | US_area_codes["317"] = ["IN","Cent. Indiana: Indianapolis"] | |
70 | US_area_codes["318"] = ["LA","N Louisiana: Shreveport, Ruston, Monroe, Alexandria"] | |
71 | US_area_codes["319"] = ["IA","E Iowa: Cedar Rapids"] | |
72 | US_area_codes["320"] = ["MN","Cent. Minnesota: Saint Cloud"] | |
73 | US_area_codes["321"] = ["FL","Florida: Brevard County, Cape Canaveral area; Metro Orlando"] | |
74 | US_area_codes["323"] = ["CA","S California: Los Angeles"] | |
75 | US_area_codes["325"] = ["TX","Central Texas: Abilene, Sweetwater, Snyder, San Angelo"] | |
76 | US_area_codes["330"] = ["OH","NE Ohio: Akron, Canton, Youngstown; Mahoning County, parts of Trumbull/Warren counties"] | |
77 | US_area_codes["331"] = ["IL","W NE Illinois, western suburbs of Chicago"] | |
78 | US_area_codes["334"] = ["AL","S Alabama: Auburn/Opelika, Montgomery and coastal areas"] | |
79 | US_area_codes["336"] = ["NC","Cent. North Carolina: Greensboro, Winston-Salem, High Point"] | |
80 | US_area_codes["337"] = ["LA","SW Louisiana: Lake Charles, Lafayette"] | |
81 | US_area_codes["339"] = ["MA","Massachusetts: Boston suburbs, to the south and west"] | |
82 | US_area_codes["340"] = ["VI","US Virgin Islands"] | |
83 | US_area_codes["341"] = ["CA","(overlay on 510; SUSPENDED)"] | |
84 | US_area_codes["345"] = ["--","Cayman Islands"] | |
85 | US_area_codes["347"] = ["NY","New York"] | |
86 | US_area_codes["351"] = ["MA","Massachusetts: north of Boston to NH, 508, and 781"] | |
87 | US_area_codes["352"] = ["FL","Florida: Gainesville area, Ocala, Crystal River"] | |
88 | US_area_codes["360"] = ["WA","W Washington State: Olympia, Bellingham"] | |
89 | US_area_codes["361"] = ["TX","S Texas: Corpus Christi"] | |
90 | US_area_codes["369"] = ["CA","Solano County"] | |
91 | US_area_codes["380"] = ["OH","Ohio: Columbus"] | |
92 | US_area_codes["385"] = ["UT","Utah: Salt Lake City Metro"] | |
93 | US_area_codes["386"] = ["FL","N central Florida: Lake City"] | |
94 | US_area_codes["401"] = ["RI","Rhode Island"] | |
95 | US_area_codes["402"] = ["NE","E Nebraska: Omaha, Lincoln"] | |
96 | US_area_codes["403"] = ["AB","Canada: Southern Alberta"] | |
97 | US_area_codes["404"] = ["GA","N Georgia: Atlanta and suburbs"] | |
98 | US_area_codes["405"] = ["OK","W Oklahoma: Oklahoma City"] | |
99 | US_area_codes["406"] = ["MT","Montana"] | |
100 | US_area_codes["407"] = ["FL","Central Florida: Metro Orlando"] | |
101 | US_area_codes["408"] = ["CA","Cent. Coastal California: San Jose"] | |
102 | US_area_codes["409"] = ["TX","SE Texas: Galveston, Port Arthur, Beaumont"] | |
103 | US_area_codes["410"] = ["MD","E Maryland: Baltimore, Annapolis, Chesapeake Bay area, Ocean City"] | |
104 | US_area_codes["411"] = ["--","Reserved for special applications"] | |
105 | US_area_codes["412"] = ["PA","W Pennsylvania: Pittsburgh"] | |
106 | US_area_codes["413"] = ["MA","W Massachusetts: Springfield"] | |
107 | US_area_codes["414"] = ["WI","SE Wisconsin: Milwaukee County"] | |
108 | US_area_codes["415"] = ["CA","California: San Francisco County and Marin County on the north side of the Golden Gate Bridge, extending north to Sonoma County"] | |
109 | US_area_codes["416"] = ["ON","Canada: S Cent. Ontario: Toronto"] | |
110 | US_area_codes["417"] = ["MO","SW Missouri: Springfield"] | |
111 | US_area_codes["418"] = ["QC","Canada: NE Quebec: Quebec"] | |
112 | US_area_codes["419"] = ["OH","NW Ohio: Toledo"] | |
113 | US_area_codes["423"] = ["TN","E Tennessee, except Knoxville metro area: Chattanooga, Bristol, Johnson City, Kingsport, Greeneville"] | |
114 | US_area_codes["424"] = ["CA","S California: Los Angeles"] | |
115 | US_area_codes["425"] = ["WA","Washington: North Tier - Everett, Bellevue"] | |
116 | US_area_codes["430"] = ["TX","NE Texas: Tyler"] | |
117 | US_area_codes["432"] = ["TX","W Texas: Big Spring, Midland, Odessa"] | |
118 | US_area_codes["434"] = ["VA","E Virginia: Charlottesville, Lynchburg, Danville, South Boston, and Emporia"] | |
119 | US_area_codes["435"] = ["UT","Rural Utah outside Salt Lake City metro"] | |
120 | US_area_codes["438"] = ["QC","Canada: SW Quebec: Montreal city"] | |
121 | US_area_codes["440"] = ["OH","Ohio: Cleveland metro area, excluding Cleveland"] | |
122 | US_area_codes["441"] = ["--","Bermuda"] | |
123 | US_area_codes["442"] = ["CA","Far north suburbs of San Diego"] | |
124 | US_area_codes["443"] = ["MD","E Maryland: Baltimore, Annapolis, Chesapeake Bay area, Ocean City"] | |
125 | US_area_codes["450"] = ["QC","Canada: Southeastern Quebec; suburbs outside metro Montreal"] | |
126 | US_area_codes["456"] = ["--","Inbound International"] | |
127 | US_area_codes["464"] = ["IL","Illinois: south suburbs of Chicago"] | |
128 | US_area_codes["469"] = ["TX","Texas: Dallas Metro"] | |
129 | US_area_codes["470"] = ["GA","Georgia: Greater Atlanta Metropolitan Area"] | |
130 | US_area_codes["473"] = ["--","Grenada"] | |
131 | US_area_codes["475"] = ["CT","Connecticut: New Haven, Greenwich, southwestern"] | |
132 | US_area_codes["478"] = ["GA","Central Georgia: Macon"] | |
133 | US_area_codes["479"] = ["AR","NW Arkansas: Fort Smith, Fayetteville, Springdale, Bentonville"] | |
134 | US_area_codes["480"] = ["AZ","Arizona: East Phoenix"] | |
135 | US_area_codes["484"] = ["PA","SE Pennsylvania: Allentown, Bethlehem, Reading, West Chester, Norristown"] | |
136 | US_area_codes["500"] = ["--","Personal Communication Service"] | |
137 | US_area_codes["501"] = ["AR","Central Arkansas: Little Rock, Hot Springs, Conway"] | |
138 | US_area_codes["502"] = ["KY","N Central Kentucky: Louisville"] | |
139 | US_area_codes["503"] = ["OR","Oregon"] | |
140 | US_area_codes["504"] = ["LA","E Louisiana: New Orleans metro area"] | |
141 | US_area_codes["505"] = ["NM","North central and northwestern New Mexico"] | |
142 | US_area_codes["506"] = ["NB","Canada: New Brunswick"] | |
143 | US_area_codes["507"] = ["MN","S Minnesota: Rochester, Mankato, Worthington"] | |
144 | US_area_codes["508"] = ["MA","Cent. Massachusetts: Framingham; Cape Cod"] | |
145 | US_area_codes["509"] = ["WA","E and Central Washington state: Spokane, Yakima, Walla Walla, Ellensburg"] | |
146 | US_area_codes["510"] = ["CA","California: Oakland, East Bay"] | |
147 | US_area_codes["511"] = ["--","Nationwide travel information"] | |
148 | US_area_codes["512"] = ["TX","S Texas: Austin"] | |
149 | US_area_codes["513"] = ["OH","SW Ohio: Cincinnati"] | |
150 | US_area_codes["514"] = ["QC","Canada: SW Quebec: Montreal city"] | |
151 | US_area_codes["515"] = ["IA","Cent. Iowa: Des Moines"] | |
152 | US_area_codes["516"] = ["NY","New York: Nassau County, Long Island; Hempstead"] | |
153 | US_area_codes["517"] = ["MI","Cent. Michigan: Lansing"] | |
154 | US_area_codes["518"] = ["NY","NE New York: Albany"] | |
155 | US_area_codes["519"] = ["ON","Canada: SW Ontario: Windsor"] | |
156 | US_area_codes["520"] = ["AZ","SE Arizona: Tucson area"] | |
157 | US_area_codes["530"] = ["CA","NE California: Eldorado County area, excluding Eldorado Hills itself: incl cities of Auburn, Chico, Redding, So. Lake Tahoe, Marysville, Nevada City/Grass Valley"] | |
158 | US_area_codes["539"] = ["OK","E Oklahoma: Tulsa area"] | |
159 | US_area_codes["540"] = ["VA","Western and Southwest Virginia: Shenandoah and Roanoke valleys: Fredericksburg, Harrisonburg, Roanoke, Salem, Lexington and nearby areas"] | |
160 | US_area_codes["541"] = ["OR","Oregon: Eugene, Medford"] | |
161 | US_area_codes["551"] = ["NJ","N New Jersey: Jersey City, Hackensack"] | |
162 | US_area_codes["555"] = ["--","Reserved for directory assistance applications"] | |
163 | US_area_codes["557"] = ["MO","SE Missouri: St Louis metro area only"] | |
164 | US_area_codes["559"] = ["CA","Central California: Fresno"] | |
165 | US_area_codes["561"] = ["FL","S. Central Florida: Palm Beach County"] | |
166 | US_area_codes["562"] = ["CA","California: Long Beach"] | |
167 | US_area_codes["563"] = ["IA","E Iowa: Davenport, Dubuque"] | |
168 | US_area_codes["564"] = ["WA","W Washington State: Olympia, Bellingham"] | |
169 | US_area_codes["567"] = ["OH","NW Ohio: Toledo"] | |
170 | US_area_codes["570"] = ["PA","NE and N Central Pennsylvania: Wilkes-Barre, Scranton"] | |
171 | US_area_codes["571"] = ["VA","Northern Virginia: Arlington, McLean, Tysons Corner"] | |
172 | US_area_codes["573"] = ["MO","SE Missouri: excluding St Louis metro area, includes Central/East Missouri, area between St. Louis and Kansas City"] | |
173 | US_area_codes["574"] = ["IN","N Indiana: Elkhart, South Bend"] | |
174 | US_area_codes["575"] = ["NM","New Mexico"] | |
175 | US_area_codes["580"] = ["OK","W Oklahoma"] | |
176 | US_area_codes["585"] = ["NY","NW New York: Rochester"] | |
177 | US_area_codes["586"] = ["MI","Michigan: Macomb County"] | |
178 | US_area_codes["600"] = ["--","Canadian Services"] | |
179 | US_area_codes["601"] = ["MS","Mississippi: Meridian, Jackson area"] | |
180 | US_area_codes["602"] = ["AZ","Arizona: Phoenix"] | |
181 | US_area_codes["603"] = ["NH","New Hampshire"] | |
182 | US_area_codes["604"] = ["BC","Canada: British Columbia: Greater Vancouver"] | |
183 | US_area_codes["605"] = ["SD","South Dakota"] | |
184 | US_area_codes["606"] = ["KY","E Kentucky: area east of Frankfort: Ashland"] | |
185 | US_area_codes["607"] = ["NY","S Cent. New York: Ithaca, Binghamton; Catskills"] | |
186 | US_area_codes["608"] = ["WI","SW Wisconsin: Madison"] | |
187 | US_area_codes["609"] = ["NJ","S New Jersey: Trenton"] | |
188 | US_area_codes["610"] = ["PA","SE Pennsylvania: Allentown, Bethlehem, Reading, West Chester, Norristown"] | |
189 | US_area_codes["611"] = ["--","Reserved for special applications"] | |
190 | US_area_codes["612"] = ["MN","Cent. Minnesota: Minneapolis"] | |
191 | US_area_codes["613"] = ["ON","Canada: SE Ontario: Ottawa"] | |
192 | US_area_codes["614"] = ["OH","SE Ohio: Columbus"] | |
193 | US_area_codes["615"] = ["TN","Northern Middle Tennessee: Nashville metro area"] | |
194 | US_area_codes["616"] = ["MI","W Michigan: Holland, Grand Haven, Greenville, Grand Rapids, Ionia"] | |
195 | US_area_codes["617"] = ["MA","Massachusetts: greater Boston"] | |
196 | US_area_codes["618"] = ["IL","S Illinois: Centralia"] | |
197 | US_area_codes["619"] = ["CA","S California: San Diego"] | |
198 | US_area_codes["620"] = ["KS","S Kansas: Wichita"] | |
199 | US_area_codes["623"] = ["AZ","Arizona: West Phoenix"] | |
200 | US_area_codes["626"] = ["CA","E S California: Pasadena"] | |
201 | US_area_codes["627"] = ["CA","No longer in use [was Napa, Sonoma counties"] | |
202 | US_area_codes["628"] = ["CA","(Region unknown; perm 10/21/00)21/00)00)"] | |
203 | US_area_codes["630"] = ["IL","W NE Illinois, western suburbs of Chicago"] | |
204 | US_area_codes["631"] = ["NY","New York: Suffolk County, Long Island; Huntington, Riverhead"] | |
205 | US_area_codes["636"] = ["MO","Missouri: W St. Louis metro area of St. Louis county, St. Charles County, Jefferson County area south"] | |
206 | US_area_codes["641"] = ["IA","Iowa: Mason City, Marshalltown, Creston, Ottumwa"] | |
207 | US_area_codes["646"] = ["NY","New York"] | |
208 | US_area_codes["647"] = ["ON","Canada: S Cent. Ontario: Toronto"] | |
209 | US_area_codes["649"] = ["--","Turks & Caicos Islands"] | |
210 | US_area_codes["650"] = ["CA","California: Peninsula south of San Francisco -- San Mateo County, parts of Santa Clara County"] | |
211 | US_area_codes["651"] = ["MN","Cent. Minnesota: St. Paul"] | |
212 | US_area_codes["660"] = ["MO","N Missouri"] | |
213 | US_area_codes["661"] = ["CA","California: N Los Angeles, Mckittrick, Mojave, Newhall, Oildale, Palmdale, Taft, Tehachapi, Bakersfield, Earlimart, Lancaster"] | |
214 | US_area_codes["662"] = ["MS","N Mississippi: Tupelo, Grenada"] | |
215 | US_area_codes["664"] = ["--","Montserrat"] | |
216 | US_area_codes["669"] = ["CA","Cent. Coastal California: San Jose"] | |
217 | US_area_codes["670"] = ["MP","Commonwealth of the Northern Mariana Islands (CNMI, US Commonwealth)"] | |
218 | US_area_codes["671"] = ["GU","Guam"] | |
219 | US_area_codes["678"] = ["GA","N Georgia: metropolitan Atlanta"] | |
220 | US_area_codes["679"] = ["MI","Michigan: Dearborn area"] | |
221 | US_area_codes["682"] = ["TX","Texas: Fort Worth areas"] | |
222 | US_area_codes["684"] = ["--","American Samoa"] | |
223 | US_area_codes["689"] = ["FL","Central Florida: Metro Orlando"] | |
224 | US_area_codes["700"] = ["--","Interexchange Carrier Services"] | |
225 | US_area_codes["701"] = ["ND","North Dakota"] | |
226 | US_area_codes["702"] = ["NV","S. Nevada: Clark County, incl Las Vegas"] | |
227 | US_area_codes["703"] = ["VA","Northern Virginia: Arlington, McLean, Tysons Corner"] | |
228 | US_area_codes["704"] = ["NC","W North Carolina: Charlotte"] | |
229 | US_area_codes["705"] = ["ON","Canada: NE Ontario: Sault Ste. Marie/N Ontario: N Bay, Sudbury"] | |
230 | US_area_codes["706"] = ["GA","N Georgia: Columbus, Augusta"] | |
231 | US_area_codes["707"] = ["CA","NW California: Santa Rosa, Napa, Vallejo, American Canyon, Fairfield"] | |
232 | US_area_codes["708"] = ["IL","Illinois: southern and western suburbs of Chicago"] | |
233 | US_area_codes["709"] = ["NL","Canada: Newfoundland and Labrador"] | |
234 | US_area_codes["710"] = ["--","US Government"] | |
235 | US_area_codes["711"] = ["--","Telecommunications Relay Services"] | |
236 | US_area_codes["712"] = ["IA","W Iowa: Council Bluffs"] | |
237 | US_area_codes["713"] = ["TX","Mid SE Texas: central Houston"] | |
238 | US_area_codes["714"] = ["CA","North and Central Orange County"] | |
239 | US_area_codes["715"] = ["WI","N Wisconsin: Eau Claire, Wausau, Superior"] | |
240 | US_area_codes["716"] = ["NY","NW New York: Buffalo"] | |
241 | US_area_codes["717"] = ["PA","E Pennsylvania: Harrisburg"] | |
242 | US_area_codes["718"] = ["NY","New York City, New York"] | |
243 | US_area_codes["719"] = ["CO","SE Colorado: Pueblo, Colorado Springs"] | |
244 | US_area_codes["720"] = ["CO","Central Colorado: Denver"] | |
245 | US_area_codes["724"] = ["PA","SW Pennsylvania"] | |
246 | US_area_codes["727"] = ["FL","Florida Tampa Metro: Saint Petersburg, Clearwater"] | |
247 | US_area_codes["731"] = ["TN","W Tennessee: outside Memphis metro area"] | |
248 | US_area_codes["732"] = ["NJ","Cent. New Jersey: Toms River, New Brunswick, Bound Brook"] | |
249 | US_area_codes["734"] = ["MI","SE Michigan: west and south of Detroit -- Ann Arbor, Monroe"] | |
250 | US_area_codes["737"] = ["TX","S Texas: Austin"] | |
251 | US_area_codes["740"] = ["OH","SE Ohio"] | |
252 | US_area_codes["747"] = ["CA","S California: Los Angeles, Agoura Hills, Calabasas, Hidden Hills, and Westlake Village"] | |
253 | US_area_codes["754"] = ["FL","Florida: Broward County area, incl Ft. Lauderdale"] | |
254 | US_area_codes["757"] = ["VA","E Virginia: Tidewater / Hampton Roads area -- Norfolk, Virginia Beach, Chesapeake, Portsmouth, Hampton, Newport News, Suffolk"] | |
255 | US_area_codes["758"] = ["--","St. Lucia"] | |
256 | US_area_codes["760"] = ["CA","California: San Diego North County to Sierra Nevada"] | |
257 | US_area_codes["762"] = ["GA","N Georgia: Columbus, Augusta"] | |
258 | US_area_codes["763"] = ["MN","Minnesota: Minneapolis NW"] | |
259 | US_area_codes["764"] = ["CA","SUSPENDED"] | |
260 | US_area_codes["765"] = ["IN","Indiana: outside Indianapolis"] | |
261 | US_area_codes["767"] = ["--","Dominica"] | |
262 | US_area_codes["769"] = ["MS","Mississippi: Meridian, Jackson area"] | |
263 | US_area_codes["770"] = ["GA","Georgia: Atlanta suburbs: outside of I-285 ring road"] | |
264 | US_area_codes["772"] = ["FL","S. Central Florida: St. Lucie, Martin, and Indian River counties"] | |
265 | US_area_codes["773"] = ["IL","Illinois: city of Chicago, outside the loop"] | |
266 | US_area_codes["774"] = ["MA","Cent. Massachusetts: Framingham; Cape Cod"] | |
267 | US_area_codes["775"] = ["NV","N. Nevada: Reno"] | |
268 | US_area_codes["778"] = ["BC","Canada: British Columbia: Greater Vancouver"] | |
269 | US_area_codes["779"] = ["IL","NW Illinois: Rockford, Kankakee"] | |
270 | US_area_codes["780"] = ["AB","Canada: Northern Alberta, north of Lacombe"] | |
271 | US_area_codes["781"] = ["MA","Massachusetts: Boston surburbs, to the north and west"] | |
272 | US_area_codes["784"] = ["--","St. Vincent & Grenadines"] | |
273 | US_area_codes["785"] = ["KS","N & W Kansas: Topeka"] | |
274 | US_area_codes["786"] = ["FL","SE Florida, Monroe County"] | |
275 | US_area_codes["787"] = ["PR","Puerto Rico"] | |
276 | US_area_codes["800"] = ["--","US/Canada toll free"] | |
277 | US_area_codes["801"] = ["UT","Utah: Salt Lake City Metro"] | |
278 | US_area_codes["802"] = ["VT","Vermont"] | |
279 | US_area_codes["803"] = ["SC","South Carolina: Columbia, Aiken, Sumter"] | |
280 | US_area_codes["804"] = ["VA","E Virginia: Richmond"] | |
281 | US_area_codes["805"] = ["CA","S Cent. and Cent. Coastal California: Ventura County, Santa Barbara County: San Luis Obispo, Thousand Oaks, Carpinteria, Santa Barbara, Santa Maria, Lompoc, Santa Ynez Valley / Solvang"] | |
282 | US_area_codes["806"] = ["TX","Panhandle Texas: Amarillo, Lubbock"] | |
283 | US_area_codes["807"] = ["ON","Canada: W Ontario: Thunder Bay region to Manitoba border"] | |
284 | US_area_codes["808"] = ["HI","Hawaii"] | |
285 | US_area_codes["809"] = ["--","Dominican Republic"] | |
286 | US_area_codes["810"] = ["MI","E Michigan: Flint, Pontiac"] | |
287 | US_area_codes["811"] = ["--","Reserved for special applications"] | |
288 | US_area_codes["812"] = ["IN","S Indiana: Evansville, Cincinnati outskirts in IN, Columbus, Bloomington"] | |
289 | US_area_codes["813"] = ["FL","SW Florida: Tampa Metro"] | |
290 | US_area_codes["814"] = ["PA","Cent. Pennsylvania: Erie"] | |
291 | US_area_codes["815"] = ["IL","NW Illinois: Rockford, Kankakee"] | |
292 | US_area_codes["816"] = ["MO","N Missouri: Kansas City"] | |
293 | US_area_codes["817"] = ["TX","N Cent. Texas: Fort Worth area"] | |
294 | US_area_codes["818"] = ["CA","S California: Los Angeles: San Fernando Valley"] | |
295 | US_area_codes["819"] = ["QC","NW Quebec: Trois Rivieres, Sherbrooke, Outaouais"] | |
296 | US_area_codes["822"] = ["--","US/Canada toll free"] | |
297 | US_area_codes["828"] = ["NC","W North Carolina: Asheville"] | |
298 | US_area_codes["829"] = ["--","Dominican Republic"] | |
299 | US_area_codes["830"] = ["TX","Texas: region surrounding San Antonio"] | |
300 | US_area_codes["831"] = ["CA","California: central coast area from Santa Cruz through Monterey County"] | |
301 | US_area_codes["832"] = ["TX","Texas: Houston"] | |
302 | US_area_codes["833"] = ["--","US/Canada toll free"] | |
303 | US_area_codes["835"] = ["PA","SE Pennsylvania: Allentown, Bethlehem, Reading, West Chester, Norristown"] | |
304 | US_area_codes["843"] = ["SC","South Carolina, coastal area: Charleston, Beaufort, Myrtle Beach"] | |
305 | US_area_codes["844"] = ["--","US/Canada toll free"] | |
306 | US_area_codes["845"] = ["NY","New York: Poughkeepsie; Nyack, Nanuet, Valley Cottage, New City, Putnam, Dutchess, Rockland, Orange, Ulster and parts of Sullivan counties in New York's lower Hudson Valley and Delaware County in the Catskills"] | |
307 | US_area_codes["847"] = ["IL","Northern NE Illinois: northwestern suburbs of chicago"] | |
308 | US_area_codes["848"] = ["NJ","Cent. New Jersey: Toms River, New Brunswick, Bound Brook"] | |
309 | US_area_codes["850"] = ["FL","Florida panhandle, from east of Tallahassee to Pensacola"] | |
310 | US_area_codes["855"] = ["--","US/Canada toll free"] | |
311 | US_area_codes["856"] = ["NJ","SW New Jersey: greater Camden area, Mt Laurel"] | |
312 | US_area_codes["857"] = ["MA","Massachusetts: greater Boston"] | |
313 | US_area_codes["858"] = ["CA","S California: San Diego"] | |
314 | US_area_codes["859"] = ["KY","N and Central Kentucky: Lexington; suburban KY counties of Cincinnati OH metro area; Covington, Newport, Ft. Thomas, Ft. Wright, Florence"] | |
315 | US_area_codes["860"] = ["CT","Connecticut: areas outside of Fairfield and New Haven Counties"] | |
316 | US_area_codes["862"] = ["NJ","N New Jersey: Newark Paterson Morristown"] | |
317 | US_area_codes["863"] = ["FL","Florida: Lakeland, Polk County"] | |
318 | US_area_codes["864"] = ["SC","South Carolina, upstate area: Greenville, Spartanburg"] | |
319 | US_area_codes["865"] = ["TN","E Tennessee: Knoxville, Knox and adjacent counties"] | |
320 | US_area_codes["866"] = ["--","US/Canada toll free"] | |
321 | US_area_codes["867"] = ["YT","Canada: Yukon, Northwest Territories, Nunavut"] | |
322 | US_area_codes["868"] = ["--","Trinidad and Tobago"] | |
323 | US_area_codes["869"] = ["--","St. Kitts & Nevis"] | |
324 | US_area_codes["870"] = ["AR","Arkansas: areas outside of west/central AR: Jonesboro, etc"] | |
325 | US_area_codes["872"] = ["IL","Illinois: Chicago"] | |
326 | US_area_codes["876"] = ["--","Jamaica"] | |
327 | US_area_codes["877"] = ["--","US/Canada toll free"] | |
328 | US_area_codes["878"] = ["PA","Pittsburgh, New Castle"] | |
329 | US_area_codes["880"] = ["--","Paid Toll-Free Service"] | |
330 | US_area_codes["881"] = ["--","Paid Toll-Free Service"] | |
331 | US_area_codes["882"] = ["--","Paid Toll-Free Service"] | |
332 | US_area_codes["888"] = ["--","US/Canada toll free"] | |
333 | US_area_codes["898"] = ["--","VoIP service"] | |
334 | US_area_codes["900"] = ["--","US toll calls -- prices vary with the number called"] | |
335 | US_area_codes["901"] = ["TN","W Tennessee: Memphis metro area"] | |
336 | US_area_codes["902"] = ["NS","Canada: Nova Scotia, Prince Edward Island"] | |
337 | US_area_codes["903"] = ["TX","NE Texas: Tyler"] | |
338 | US_area_codes["904"] = ["FL","N Florida: Jacksonville"] | |
339 | US_area_codes["905"] = ["ON","Canada: S Cent. Ontario: Greater Toronto Area -- Durham, Halton, Hamilton-Wentworth, Niagara, Peel, York, and southern Simcoe County"] | |
340 | US_area_codes["906"] = ["MI","Upper Peninsula Michigan: Sault Ste. Marie, Escanaba, Marquette"] | |
341 | US_area_codes["907"] = ["AK","Alaska"] | |
342 | US_area_codes["908"] = ["NJ","Cent. New Jersey: Elizabeth, Basking Ridge, Somerville, Bridgewater, Bound Brook"] | |
343 | US_area_codes["909"] = ["CA","California: Inland empire: San Bernardino"] | |
344 | US_area_codes["910"] = ["NC","S Cent. North Carolina: Fayetteville, Wilmington"] | |
345 | US_area_codes["911"] = ["--","Emergency"] | |
346 | US_area_codes["912"] = ["GA","SE Georgia: Savannah"] | |
347 | US_area_codes["913"] = ["KS","Kansas: Kansas City area"] | |
348 | US_area_codes["914"] = ["NY","S New York: Westchester County"] | |
349 | US_area_codes["915"] = ["TX","W Texas: El Paso"] | |
350 | US_area_codes["916"] = ["CA","NE California: Sacramento, Walnut Grove, Lincoln, Newcastle and El Dorado Hills"] | |
351 | US_area_codes["917"] = ["NY","New York: New York City"] | |
352 | US_area_codes["918"] = ["OK","E Oklahoma: Tulsa"] | |
353 | US_area_codes["919"] = ["NC","E North Carolina: Raleigh"] | |
354 | US_area_codes["920"] = ["WI","NE Wisconsin: Appleton, Green Bay, Sheboygan, Fond du Lac"] | |
355 | US_area_codes["925"] = ["CA","California: Contra Costa area: Antioch, Concord, Pleasanton, Walnut Creek"] | |
356 | US_area_codes["927"] = ["FL","Florida: Cellular coverage in Orlando area"] | |
357 | US_area_codes["928"] = ["AZ","Central and Northern Arizona: Prescott, Flagstaff, Yuma"] | |
358 | US_area_codes["931"] = ["TN","Middle Tennessee: semi-circular ring around Nashville"] | |
359 | US_area_codes["935"] = ["CA","S California: San Diego"] | |
360 | US_area_codes["936"] = ["TX","SE Texas: Conroe, Lufkin, Nacogdoches, Crockett"] | |
361 | US_area_codes["937"] = ["OH","SW Ohio: Dayton"] | |
362 | US_area_codes["939"] = ["PR","Puerto Rico"] | |
363 | US_area_codes["940"] = ["TX","N Cent. Texas: Denton, Wichita Falls"] | |
364 | US_area_codes["941"] = ["FL","SW Florida: Sarasota and Manatee counties"] | |
365 | US_area_codes["947"] = ["MI","Michigan: Oakland County"] | |
366 | US_area_codes["949"] = ["CA","California: S Coastal Orange County"] | |
367 | US_area_codes["951"] = ["CA","California: W Riverside County"] | |
368 | US_area_codes["952"] = ["MN","Minnesota: Minneapolis SW, Bloomington"] | |
369 | US_area_codes["954"] = ["FL","Florida: Broward County area, incl Ft. Lauderdale"] | |
370 | US_area_codes["956"] = ["TX","Texas: Valley of Texas area; Harlingen, Laredo"] | |
371 | US_area_codes["957"] = ["NM","New Mexico"] | |
372 | US_area_codes["959"] = ["CT","Connecticut: Hartford, New London"] | |
373 | US_area_codes["970"] = ["CO","N and W Colorado"] | |
374 | US_area_codes["971"] = ["OR","Oregon: Metropolitan Portland, Salem/Keizer area, incl Cricket Wireless"] | |
375 | US_area_codes["972"] = ["TX","Texas: Dallas Metro"] | |
376 | US_area_codes["973"] = ["NJ","N New Jersey: Newark, Paterson, Morristown"] | |
377 | US_area_codes["975"] = ["MO","N Missouri: Kansas City"] | |
378 | US_area_codes["976"] = ["--","Unassigned"] | |
379 | US_area_codes["978"] = ["MA","Massachusetts: north of Boston to NH"] | |
380 | US_area_codes["979"] = ["TX","SE Texas: Bryan, College Station, Bay City"] | |
381 | US_area_codes["980"] = ["NC","North Carolina:"] | |
382 | US_area_codes["984"] = ["NC","E North Carolina: Raleigh"] | |
383 | US_area_codes["985"] = ["LA","E Louisiana: SE/N shore of Lake Pontchartrain: Hammond, Slidell, Covington, Amite, Kentwood, area SW of New Orleans, Houma, Thibodaux, Morgan City"] | |
384 | US_area_codes["989"] = ["MI","Upper central Michigan: Mt Pleasant, Saginaw"] | |
385 | US_area_codes["999"] = ["--","Often used by carriers to indicate that the area code information is unavailable for CNID, even though the rest of the number is present"] |