ronin-support 0.2.0 → 0.3.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.
- data/.gitignore +11 -0
- data/ChangeLog.md +42 -1
- data/README.md +4 -1
- data/gemspec.yml +2 -1
- data/lib/ronin/extensions.rb +2 -0
- data/lib/ronin/extensions/enumerable.rb +54 -0
- data/lib/ronin/extensions/file.rb +70 -2
- data/lib/ronin/extensions/ip_addr.rb +45 -45
- data/lib/ronin/extensions/regexp.rb +45 -0
- data/lib/ronin/extensions/resolv.rb +80 -0
- data/lib/ronin/extensions/string.rb +35 -32
- data/lib/ronin/formatting/extensions/binary/integer.rb +12 -5
- data/lib/ronin/formatting/extensions/binary/string.rb +44 -16
- data/lib/ronin/formatting/extensions/html/integer.rb +51 -31
- data/lib/ronin/formatting/extensions/html/string.rb +50 -31
- data/lib/ronin/formatting/extensions/http/integer.rb +10 -2
- data/lib/ronin/formatting/extensions/sql.rb +20 -0
- data/lib/ronin/formatting/extensions/sql/string.rb +98 -0
- data/lib/ronin/formatting/extensions/text/array.rb +11 -9
- data/lib/ronin/formatting/extensions/text/string.rb +213 -29
- data/lib/ronin/formatting/sql.rb +20 -0
- data/lib/ronin/network/extensions/http.rb +1 -0
- data/lib/ronin/network/extensions/http/net.rb +2 -2
- data/lib/ronin/network/extensions/http/uri/http.rb +226 -0
- data/lib/ronin/network/extensions/imap/net.rb +1 -1
- data/lib/ronin/network/extensions/ssl/net.rb +7 -1
- data/lib/ronin/network/http/proxy.rb +20 -21
- data/lib/ronin/network/mixins.rb +27 -0
- data/lib/ronin/network/mixins/esmtp.rb +165 -0
- data/lib/ronin/network/mixins/http.rb +723 -0
- data/lib/ronin/network/mixins/imap.rb +151 -0
- data/lib/ronin/network/mixins/pop3.rb +141 -0
- data/lib/ronin/network/mixins/smtp.rb +159 -0
- data/lib/ronin/network/mixins/tcp.rb +331 -0
- data/lib/ronin/network/mixins/telnet.rb +199 -0
- data/lib/ronin/network/mixins/udp.rb +227 -0
- data/lib/ronin/network/ssl.rb +17 -11
- data/lib/ronin/path.rb +3 -3
- data/lib/ronin/spec/ui/output.rb +28 -0
- data/lib/ronin/support.rb +3 -0
- data/lib/ronin/support/version.rb +1 -1
- data/lib/ronin/ui/output.rb +21 -0
- data/lib/ronin/ui/output/helpers.rb +248 -0
- data/lib/ronin/ui/output/output.rb +146 -0
- data/lib/ronin/ui/output/terminal.rb +21 -0
- data/lib/ronin/ui/output/terminal/color.rb +118 -0
- data/lib/ronin/ui/output/terminal/raw.rb +103 -0
- data/lib/ronin/ui/shell.rb +219 -0
- data/ronin-support.gemspec +1 -1
- data/spec/extensions/enumerable_spec.rb +24 -0
- data/spec/extensions/file_spec.rb +39 -0
- data/spec/extensions/ip_addr_spec.rb +6 -0
- data/spec/extensions/resolv_spec.rb +18 -0
- data/spec/formatting/html/integer_spec.rb +2 -2
- data/spec/formatting/html/string_spec.rb +1 -1
- data/spec/formatting/sql/string_spec.rb +55 -0
- data/spec/formatting/text/string_spec.rb +110 -0
- data/spec/network/ssl_spec.rb +10 -4
- data/spec/ui/classes/test_shell.rb +22 -0
- data/spec/ui/output_spec.rb +32 -0
- data/spec/ui/shell_spec.rb +79 -0
- metadata +132 -90
data/.gitignore
ADDED
data/ChangeLog.md
CHANGED
@@ -1,4 +1,45 @@
|
|
1
|
-
### 0.
|
1
|
+
### 0.3.0 / 2011-10-16
|
2
|
+
|
3
|
+
* Require combinatorics ~> 0.4.
|
4
|
+
* Added {Enumerable#map_hash}.
|
5
|
+
* Added {String.generate}.
|
6
|
+
* Added {String#fuzz}.
|
7
|
+
* Added {File.each_line}.
|
8
|
+
* Added {File.each_row}.
|
9
|
+
* Added {Resolv.resolver}.
|
10
|
+
* Added {URI::HTTP#request}.
|
11
|
+
* Added {URI::HTTP#status}.
|
12
|
+
* Added {URI::HTTP#ok?}.
|
13
|
+
* Added {URI::HTTP#server}.
|
14
|
+
* Added {URI::HTTP#powered_by}.
|
15
|
+
* Added {URI::HTTP#copy}.
|
16
|
+
* Added {URI::HTTP#delete}.
|
17
|
+
* Added {URI::HTTP#get}.
|
18
|
+
* Added {URI::HTTP#get_headers}.
|
19
|
+
* Added {URI::HTTP#get_body}.
|
20
|
+
* Added {URI::HTTP#head}.
|
21
|
+
* Added {URI::HTTP#lock}.
|
22
|
+
* Added {URI::HTTP#mkcol}.
|
23
|
+
* Added {URI::HTTP#move}.
|
24
|
+
* Added {URI::HTTP#options}.
|
25
|
+
* Added {URI::HTTP#post}.
|
26
|
+
* Added {URI::HTTP#post_headers}.
|
27
|
+
* Added {URI::HTTP#post_body}.
|
28
|
+
* Added {URI::HTTP#prop_find}.
|
29
|
+
* Added {URI::HTTP#prop_match}.
|
30
|
+
* Added {URI::HTTP#trace}.
|
31
|
+
* Added {URI::HTTP#unlock}.
|
32
|
+
* Added {Regexp::MAC}.
|
33
|
+
* Added {Regexp::IPv6}, {Regexp::IPv4} and {Regexp::IP}.
|
34
|
+
* Added {Regexp::HOST_NAME}.
|
35
|
+
* Added {Regexp::USER_NAME}.
|
36
|
+
* Added {Regexp::EMAIL_ADDR}.
|
37
|
+
* Moved {Ronin::UI::Output}, {Ronin::UI::Shell} and {Ronin::Network::Mixins}
|
38
|
+
from ronin into ronin-support.
|
39
|
+
* Refactored {Ronin::UI::Shell} into a Class where commands are defined as
|
40
|
+
protected methods.
|
41
|
+
|
42
|
+
### 0.2.0 / 2011-07-04
|
2
43
|
|
3
44
|
* Require data_paths ~> 0.3.
|
4
45
|
* Added {Ronin::Mixin}.
|
data/README.md
CHANGED
@@ -25,6 +25,7 @@ or payloads over many common Source-Code-Management (SCM) systems.
|
|
25
25
|
* URIs
|
26
26
|
* HTML
|
27
27
|
* JavaScript
|
28
|
+
* SQL
|
28
29
|
* Generating random text.
|
29
30
|
* Networking:
|
30
31
|
* TCP
|
@@ -53,11 +54,13 @@ please see [Everyday Ronin](http://ronin-ruby.github.com/guides/everyday_ronin.h
|
|
53
54
|
* [hexdump](http://github.com/postmodern/hexdump#readme)
|
54
55
|
~> 0.1
|
55
56
|
* [combinatorics](http://github.com/postmodern/combinatorics#readme)
|
56
|
-
~> 0.
|
57
|
+
~> 0.4
|
57
58
|
* [uri-query_params](http://github.com/postmodern/uri-query_params#readme)
|
58
59
|
~> 0.5, >= 0.5.2
|
59
60
|
* [data_paths](http://github.com/postmodern/data_paths#readme)
|
60
61
|
~> 0.3
|
62
|
+
* [parameters](http://github.com/postmodern/parameters#readme)
|
63
|
+
~> 0.2, >= 0.2.3
|
61
64
|
|
62
65
|
## Install
|
63
66
|
|
data/gemspec.yml
CHANGED
@@ -15,9 +15,10 @@ required_ruby_version: ">= 1.8.7"
|
|
15
15
|
dependencies:
|
16
16
|
chars: ~> 0.2
|
17
17
|
hexdump: ~> 0.1
|
18
|
-
combinatorics: ~> 0.
|
18
|
+
combinatorics: ~> 0.4
|
19
19
|
uri-query_params: ~> 0.5, >= 0.5.2
|
20
20
|
data_paths: ~> 0.3
|
21
|
+
parameters: ~> 0.2, >= 0.2.3
|
21
22
|
|
22
23
|
development_dependencies:
|
23
24
|
bundler: ~> 1.0.10
|
data/lib/ronin/extensions.rb
CHANGED
@@ -19,8 +19,10 @@
|
|
19
19
|
|
20
20
|
require 'ronin/extensions/meta'
|
21
21
|
require 'ronin/extensions/string'
|
22
|
+
require 'ronin/extensions/regexp'
|
22
23
|
require 'ronin/extensions/file'
|
23
24
|
require 'ronin/extensions/ip_addr'
|
25
|
+
require 'ronin/extensions/resolv'
|
24
26
|
require 'ronin/extensions/kernel'
|
25
27
|
|
26
28
|
require 'hexdump/extensions'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
|
3
|
+
#
|
4
|
+
# This file is part of Ronin Support.
|
5
|
+
#
|
6
|
+
# Ronin Support is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Ronin Support is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
|
20
|
+
module Enumerable
|
21
|
+
|
22
|
+
#
|
23
|
+
# Maps the elements to a Hash.
|
24
|
+
#
|
25
|
+
# @yield [element]
|
26
|
+
# The given block will be passed each element.
|
27
|
+
# The return value from the block will be stored in the hash.
|
28
|
+
#
|
29
|
+
# @yieldparam [Object] element
|
30
|
+
# An element.
|
31
|
+
#
|
32
|
+
# @return [Hash{Object => Object}]
|
33
|
+
# The hash of elements and their mappings.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# host_names = %w[www.wired.com www.google.com]
|
37
|
+
# host_names.map_hash { |name| Resolv.getaddresses(name) }
|
38
|
+
# # {"www.wired.com"=>["184.84.183.17", "184.84.183.91"],
|
39
|
+
# # "www.google.com"=>["173.194.33.18", "173.194.33.17", "173.194.33.19", "173.194.33.20", "173.194.33.16"]}
|
40
|
+
#
|
41
|
+
# @since 0.3.0
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
#
|
45
|
+
def map_hash
|
46
|
+
new_hash = Hash.new do |hash,key|
|
47
|
+
hash[key] = yield(key)
|
48
|
+
end
|
49
|
+
|
50
|
+
each { |element| new_hash[element] }
|
51
|
+
return new_hash
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -19,6 +19,71 @@
|
|
19
19
|
|
20
20
|
class File
|
21
21
|
|
22
|
+
#
|
23
|
+
# Reads each line from the file.
|
24
|
+
#
|
25
|
+
# @param [String] path
|
26
|
+
# The path of the file.
|
27
|
+
#
|
28
|
+
# @yield [line]
|
29
|
+
# The given block will be passed each line.
|
30
|
+
#
|
31
|
+
# @yieldparam [String] line
|
32
|
+
# A line from the file, with the trailing newline characters removed.
|
33
|
+
#
|
34
|
+
# @return [Enumerator]
|
35
|
+
# If no block is given, an Enumerator will be returned.
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# File.each_line('passwords.txt') do |line|
|
39
|
+
# # ...
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# @since 0.3.0
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
#
|
46
|
+
def File.each_line(path)
|
47
|
+
return enum_for(:each_line,path) unless block_given?
|
48
|
+
|
49
|
+
File.open(path) do |file|
|
50
|
+
file.each_line { |line| yield line.chomp }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Reads each row from the file.
|
56
|
+
#
|
57
|
+
# @param [String] path
|
58
|
+
# The path of the file.
|
59
|
+
#
|
60
|
+
# @param [Regexp, String] separator
|
61
|
+
# The pattern to split the line by.
|
62
|
+
#
|
63
|
+
# @yield [row]
|
64
|
+
# The given block will be passed each row.
|
65
|
+
#
|
66
|
+
# @yieldparam [Array<String>] row
|
67
|
+
# A row from the file.
|
68
|
+
#
|
69
|
+
# @return [Enumerator]
|
70
|
+
# If no block is given, an Enumerator will be returned.
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# File.each_row('db_dump.txt', '|') do |row|
|
74
|
+
# # ...
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# @since 0.3.0
|
78
|
+
#
|
79
|
+
# @api public
|
80
|
+
#
|
81
|
+
def File.each_row(path,separator=/\s+/)
|
82
|
+
return enum_for(:each_row,path,separator) unless block_given?
|
83
|
+
|
84
|
+
File.each_line(path) { |line| yield line.split(separator) }
|
85
|
+
end
|
86
|
+
|
22
87
|
#
|
23
88
|
# Writes the given data to a specified path.
|
24
89
|
#
|
@@ -30,6 +95,9 @@ class File
|
|
30
95
|
#
|
31
96
|
# @return [nil]
|
32
97
|
#
|
98
|
+
# @example
|
99
|
+
# File.write('dump.txt',data)
|
100
|
+
#
|
33
101
|
# @api public
|
34
102
|
#
|
35
103
|
def File.write(path,data)
|
@@ -51,10 +119,10 @@ class File
|
|
51
119
|
path = path.to_s
|
52
120
|
|
53
121
|
# remove any \0 characters first
|
54
|
-
path.
|
122
|
+
path.tr!("\0",'')
|
55
123
|
|
56
124
|
# remove any home-dir expansions
|
57
|
-
path.gsub!(
|
125
|
+
path.gsub!('~',"\\~")
|
58
126
|
|
59
127
|
path = File.expand_path(File.join('/',path))
|
60
128
|
|
@@ -17,8 +17,10 @@
|
|
17
17
|
# along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
|
18
18
|
#
|
19
19
|
|
20
|
+
require 'ronin/extensions/resolv'
|
21
|
+
require 'ronin/extensions/regexp'
|
22
|
+
|
20
23
|
require 'ipaddr'
|
21
|
-
require 'resolv'
|
22
24
|
require 'strscan'
|
23
25
|
require 'combinatorics/list_comprehension'
|
24
26
|
|
@@ -26,14 +28,11 @@ class IPAddr
|
|
26
28
|
|
27
29
|
include Enumerable
|
28
30
|
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# A regular expression for matching IP Addresses.
|
36
|
-
REGEXP = /#{IPV4_REGEXP}|#{IPV6_REGEXP}/
|
31
|
+
# Socket families and IP address masks
|
32
|
+
MASKS = {
|
33
|
+
Socket::AF_INET => IN4MASK,
|
34
|
+
Socket::AF_INET6 => IN6MASK
|
35
|
+
}
|
37
36
|
|
38
37
|
#
|
39
38
|
# Extracts IP Addresses from text.
|
@@ -41,8 +40,8 @@ class IPAddr
|
|
41
40
|
# @param [String] text
|
42
41
|
# The text to scan for IP Addresses.
|
43
42
|
#
|
44
|
-
# @param [Symbol] version
|
45
|
-
# The version of IP Address to scan for (`:
|
43
|
+
# @param [Integer, Symbol] version
|
44
|
+
# The version of IP Address to scan for (`4`, `6`, `:v4` or `:v6`).
|
46
45
|
#
|
47
46
|
# @yield [ip]
|
48
47
|
# The given block will be passed each extracted IP Address.
|
@@ -53,29 +52,36 @@ class IPAddr
|
|
53
52
|
# @return [Array<String>]
|
54
53
|
# The IP Addresses found in the text.
|
55
54
|
#
|
55
|
+
# @example
|
56
|
+
# IPAddr.extract("Host: 127.0.0.1\n\rHost: 10.1.1.1\n\r")
|
57
|
+
# # => ["127.0.0.1", "10.1.1.1"]
|
58
|
+
#
|
59
|
+
# @example Extract only IPv4 addresses from a large amount of text.
|
60
|
+
# IPAddr.extract(text,:v4) do |ip|
|
61
|
+
# puts ip
|
62
|
+
# end
|
63
|
+
#
|
56
64
|
# @api public
|
57
65
|
#
|
58
66
|
def IPAddr.extract(text,version=nil,&block)
|
67
|
+
return enum_for(:extract,text,version).to_a unless block_given?
|
68
|
+
|
59
69
|
regexp = case version
|
60
|
-
when :ipv4
|
61
|
-
|
62
|
-
when :ipv6
|
63
|
-
|
70
|
+
when :ipv4, :v4, 4
|
71
|
+
Regexp::IPv4
|
72
|
+
when :ipv6, :v6, 6
|
73
|
+
Regexp::IPv6
|
64
74
|
else
|
65
|
-
|
75
|
+
Regexp::IP
|
66
76
|
end
|
67
77
|
|
68
|
-
|
78
|
+
scanner = StringScanner.new(text)
|
69
79
|
|
70
|
-
|
71
|
-
yield
|
72
|
-
return nil
|
73
|
-
else
|
74
|
-
ips = []
|
75
|
-
|
76
|
-
ips << parser.matched while parser.skip_until(regexp)
|
77
|
-
return ips
|
80
|
+
while scanner.skip_until(regexp)
|
81
|
+
yield scanner.matched
|
78
82
|
end
|
83
|
+
|
84
|
+
return nil
|
79
85
|
end
|
80
86
|
|
81
87
|
#
|
@@ -120,28 +126,27 @@ class IPAddr
|
|
120
126
|
return enum_for(:each,cidr_or_glob) unless block
|
121
127
|
|
122
128
|
if cidr_or_glob.include?('::')
|
123
|
-
|
129
|
+
separator = '::'
|
130
|
+
base = 16
|
131
|
+
|
132
|
+
prefix = if cidr_or_glob.start_with?('::')
|
124
133
|
'::'
|
125
134
|
else
|
126
135
|
''
|
127
136
|
end
|
128
137
|
|
129
|
-
separator = '::'
|
130
|
-
base = 16
|
131
|
-
|
132
138
|
format = lambda { |address|
|
133
139
|
prefix + address.map { |i| '%.2x' % i }.join('::')
|
134
140
|
}
|
135
141
|
else
|
136
142
|
separator = '.'
|
137
|
-
base
|
138
|
-
|
139
|
-
format = lambda { |address| address.join('.') }
|
143
|
+
base = 10
|
144
|
+
format = lambda { |address| address.join('.') }
|
140
145
|
end
|
141
146
|
|
142
147
|
# split the address
|
143
148
|
segments = cidr_or_glob.split(separator)
|
144
|
-
ranges
|
149
|
+
ranges = []
|
145
150
|
|
146
151
|
# map the components of the address to numeric ranges
|
147
152
|
segments.each do |segment|
|
@@ -159,23 +164,23 @@ class IPAddr
|
|
159
164
|
end
|
160
165
|
|
161
166
|
# cycle through the address ranges
|
162
|
-
ranges.comprehension
|
163
|
-
yield format[address]
|
164
|
-
end
|
165
|
-
|
167
|
+
ranges.comprehension { |address| yield format[address] }
|
166
168
|
return nil
|
167
169
|
end
|
168
170
|
|
169
171
|
#
|
170
172
|
# Resolves the host-names for the IP address.
|
171
173
|
#
|
174
|
+
# @param [String] nameserver
|
175
|
+
# The optional nameserver to query.
|
176
|
+
#
|
172
177
|
# @return [Array<String>]
|
173
178
|
# The host-names for the IP address.
|
174
179
|
#
|
175
180
|
# @api public
|
176
181
|
#
|
177
|
-
def lookup
|
178
|
-
Resolv.getnames(self.to_s)
|
182
|
+
def lookup(nameserver=nil)
|
183
|
+
Resolv.resolver(nameserver).getnames(self.to_s)
|
179
184
|
end
|
180
185
|
|
181
186
|
#
|
@@ -201,12 +206,7 @@ class IPAddr
|
|
201
206
|
def each
|
202
207
|
return enum_for(:each) unless block_given?
|
203
208
|
|
204
|
-
|
205
|
-
when Socket::AF_INET
|
206
|
-
family_mask = IN4MASK
|
207
|
-
when Socket::AF_INET6
|
208
|
-
family_mask = IN6MASK
|
209
|
-
end
|
209
|
+
family_mask = MASKS[@family]
|
210
210
|
|
211
211
|
(0..((~@mask_addr) & family_mask)).each do |i|
|
212
212
|
yield _to_string(@addr | i)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
|
3
|
+
#
|
4
|
+
# This file is part of Ronin Support.
|
5
|
+
#
|
6
|
+
# Ronin Support is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Ronin Support is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'ronin/extensions/resolv'
|
21
|
+
|
22
|
+
class Regexp
|
23
|
+
|
24
|
+
# Regular expression for finding MAC addresses in text
|
25
|
+
MAC = /[0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5}/
|
26
|
+
|
27
|
+
# A regular expression for matching IPv4 Addresses.
|
28
|
+
IPv4 = /[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}/
|
29
|
+
|
30
|
+
# A regular expression for matching IPv6 Addresses.
|
31
|
+
IPv6 = /:(:[0-9a-f]{1,4}){1,7}|([0-9a-f]{1,4}::?){1,7}[0-9a-f]{1,4}(:#{IPv4})?/
|
32
|
+
|
33
|
+
# A regular expression for matching IP Addresses.
|
34
|
+
IP = /#{IPv4}|#{IPv6}/
|
35
|
+
|
36
|
+
# Regular expression used to find host-names in text
|
37
|
+
HOST_NAME = /(?:[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+)*\.)+(?:#{union(Resolv::TLDS)})/
|
38
|
+
|
39
|
+
# Regular expression to match a word in the username of an email address
|
40
|
+
USER_NAME = /[A-Za-z](?:[A-Za-z0-9]+[\._-])*[A-Za-z0-9]+/
|
41
|
+
|
42
|
+
# Regular expression to find email addresses in text
|
43
|
+
EMAIL_ADDR = /#{USER_NAME}(?:\.#{USER_NAME})*\@#{HOST_NAME}/
|
44
|
+
|
45
|
+
end
|