XDCC-Fetch 1.386

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/COPYING +23 -0
  2. data/XDCC-Fetch.rbw +54 -0
  3. data/doc/ark.png +0 -0
  4. data/doc/connect_established.png +0 -0
  5. data/doc/index.html +300 -0
  6. data/doc/mega_ark.png +0 -0
  7. data/doc/package.png +0 -0
  8. data/doc/package_unknown.png +0 -0
  9. data/doc/shot1.png +0 -0
  10. data/doc/shot1_mini.png +0 -0
  11. data/doc/shot2.png +0 -0
  12. data/doc/shot2_mini.png +0 -0
  13. data/doc/xdccfetch.css +155 -0
  14. data/icons/ark.png +0 -0
  15. data/icons/ark_big.png +0 -0
  16. data/icons/camera_test.png +0 -0
  17. data/icons/cancel.png +0 -0
  18. data/icons/connect_creating.png +0 -0
  19. data/icons/connect_established.png +0 -0
  20. data/icons/connect_failed.png +0 -0
  21. data/icons/connect_no.png +0 -0
  22. data/icons/edit_add.png +0 -0
  23. data/icons/edit_remove.png +0 -0
  24. data/icons/exit.png +0 -0
  25. data/icons/fileclose.png +0 -0
  26. data/icons/folder_inbox.png +0 -0
  27. data/icons/idea.png +0 -0
  28. data/icons/mega_ark.png +0 -0
  29. data/icons/messagebox_critical.png +0 -0
  30. data/icons/messagebox_info.png +0 -0
  31. data/icons/messagebox_warning.png +0 -0
  32. data/icons/messagebox_warning_small.png +0 -0
  33. data/icons/package.png +0 -0
  34. data/icons/package_favourite.png +0 -0
  35. data/icons/package_unknown.png +0 -0
  36. data/src/Console/Console_Parser.rb +71 -0
  37. data/src/Console/XDCC_Pack_Match_Template.rb +29 -0
  38. data/src/Console/xdcc-fetch.rb +123 -0
  39. data/src/GUI/About_Dialog.rb +50 -0
  40. data/src/GUI/Application_Builder.rb +280 -0
  41. data/src/GUI/Context_Menu.rb +81 -0
  42. data/src/GUI/Custom_Tabs.rb +60 -0
  43. data/src/GUI/Dialog_Box.rb +116 -0
  44. data/src/GUI/Download_Finished_Box.rb +41 -0
  45. data/src/GUI/Empty_Text_Field_Handler.rb +86 -0
  46. data/src/GUI/Gui_Logic.rb +629 -0
  47. data/src/GUI/Icon_Loader.rb +58 -0
  48. data/src/GUI/Main_Window.rb +227 -0
  49. data/src/GUI/Packet_Item.rb +171 -0
  50. data/src/GUI/Packet_List.rb +145 -0
  51. data/src/GUI/Speed_Widget.rb +101 -0
  52. data/src/GUI/Talk_Back.rb +118 -0
  53. data/src/GUI/Toggle_Button.rb +56 -0
  54. data/src/Network/CTCP_Handler.rb +61 -0
  55. data/src/Network/DCC_File.rb +323 -0
  56. data/src/Network/DCC_Parser.rb +71 -0
  57. data/src/Network/IPAddr_Ext.rb +76 -0
  58. data/src/Network/IRC_Message.rb +161 -0
  59. data/src/Network/IRC_Server.rb +273 -0
  60. data/src/Network/IRC_Server_Respond_Map.rb +223 -0
  61. data/src/Network/IRC_User.rb +58 -0
  62. data/src/Network/TCP_Connection.rb +168 -0
  63. data/src/Network/XDCC_Announcement.rb +120 -0
  64. data/src/Network/XDCC_Announcement_Storage.rb +167 -0
  65. data/src/Network/XDCC_Download_Handler.rb +412 -0
  66. data/src/Network/XDCC_Pack.rb +58 -0
  67. data/src/Network/XDCC_Parser.rb +253 -0
  68. data/src/Translations/README +61 -0
  69. data/src/Translations/check_translations +83 -0
  70. data/src/Translations/de.rb +140 -0
  71. data/src/Translations/en.rb +145 -0
  72. data/src/Utilities/Configuration.rb +91 -0
  73. data/src/Utilities/Events.rb +87 -0
  74. data/src/Utilities/Globals.rb +138 -0
  75. data/src/Utilities/PrettyException.rb +1091 -0
  76. data/src/Utilities/Recursive_Open_Struct.rb +159 -0
  77. data/src/Utilities/Timer.rb +71 -0
  78. metadata +135 -0
@@ -0,0 +1,323 @@
1
+ # Copyright (c) 2005, Christoph Heindl
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without modification,
5
+ # are permitted provided that the following conditions are met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright notice, this list
8
+ # of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above copyright notice, this list
10
+ # of conditions and the following disclaimer in the documentation and/or other materials
11
+ # provided with the distribution.
12
+ # * Neither the name of Christoph Heindl nor the names of its contributors may be used to
13
+ # endorse or promote products derived from this software without specific prior written
14
+ # permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
17
+ # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18
+ # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
23
+ # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
+
25
+ DCC_Resume_Verification = Struct.new("DCC_Resume_Verification",
26
+ :verified,
27
+ :bytes_mine,
28
+ :md5_mine,
29
+ :bytes_their,
30
+ :md5_their)
31
+
32
+ DCC_File_Info = Struct.new("DCC_File_Info",
33
+ :status, # Status of DCC
34
+ :irc_user, # Remote irc user
35
+ :address, # Remote address and port
36
+ :file, # File handle
37
+ :joined_filename, # path to file
38
+ :filename, # filename originally sent by user
39
+ :md5, # MD5 checksom of file
40
+ :verification, # DCC_Resume_Verification struct
41
+ :skip_download, # Should this download be skipped
42
+ :skip_status, # Whats the status to be broadcasted after skip succeeds
43
+ :skip_reason, # Whats the message to be broadcasted after skip succeeds
44
+ :bytes_total, # Total number of bytes to receive
45
+ :bytes_received, # Number of bytes received so far
46
+ :progress_timestamp, # Last progress timestamp
47
+ :progress_bytes) # Number of bytes since last progress event
48
+
49
+
50
+ # Handles dcc file transactions
51
+ class DCC_File
52
+ include Event_Publisher
53
+
54
+ # -- Connectable events
55
+
56
+ # Connection to remote dcc host successful. Args: none
57
+ ON_DCC_ESTABLISHED = Event_Publisher.next_global_eventID
58
+ # Transfer complete. Args: md5 checksum or nil
59
+ ON_DCC_COMPLETED = Event_Publisher.next_global_eventID
60
+ # Download/upload progress.
61
+ # Percent completed, nr of bytes received/sent, nr of kilo bytes per second
62
+ ON_DCC_PROGRESS = Event_Publisher.next_global_eventID
63
+ # DCC error. Args: errormessage
64
+ ON_DCC_ERROR = Event_Publisher.next_global_eventID
65
+
66
+ ON_ALL_EVENTS = ON_DCC_ESTABLISHED|
67
+ ON_DCC_COMPLETED|
68
+ ON_DCC_PROGRESS|
69
+ ON_DCC_ERROR
70
+
71
+
72
+ def initialize()
73
+ @tcp_con = TCP_Connection.binary
74
+ @tcp_con.connect_to_events(TCP_Connection::ON_ALL_EVENTS, self)
75
+ self.init_events
76
+ end
77
+
78
+ # Try to download using the given info.
79
+ # Resumes the download if required
80
+ def get(info, path)
81
+ # Init some variables
82
+ @info = DCC_File_Info.new(-1,
83
+ info.irc_user,
84
+ info.dccaddress_info,
85
+ nil,
86
+ "",
87
+ nil,
88
+ nil,
89
+ DCC_Resume_Verification.new(false, 0, nil, 0, nil),
90
+ false,
91
+ ON_DCC_ERROR,
92
+ "",
93
+ 0,
94
+ 0,
95
+ nil,
96
+ 0)
97
+
98
+ # Check for correct tcp info
99
+ if !@info.address.address || !@info.address.port
100
+ @info.status = ON_DCC_ERROR
101
+ self.fire_event(ON_DCC_ERROR, $cfg.text.network.tcp.err_invalid_address)
102
+ return
103
+ end
104
+
105
+ # Check for not routable addresses if required
106
+ if $cfg.network.dcc.block_nonroutable_addresses && !@info.address.address.routable_address?
107
+ @info.status = ON_DCC_ERROR
108
+ self.fire_event(ON_DCC_ERROR, $cfg.text.network.ip.err_notroutable_ip + " (#{@info.address.address.to_s})")
109
+ return
110
+ end
111
+
112
+ # See if destination directory exists
113
+ if !path || !File.exist?(path)
114
+ @info.status = ON_DCC_ERROR
115
+ self.fire_event(ON_DCC_ERROR, $cfg.text.network.dcc.err_dir_notexist)
116
+ return
117
+ end
118
+ @info.joined_filename = File.join(path, info.filename)
119
+ @info.bytes_total = info.filesize
120
+ if File.exist?(@info.joined_filename)
121
+ self.resume
122
+ else
123
+ # No resume required.
124
+ # Init md5 if required and open connection
125
+ @info.verification.verified = true
126
+ @info.md5 = Digest::MD5.new if $cfg.network.xdcc.enable_md5
127
+ @tcp_con.open(@info.address.address.to_s, @info.address.port)
128
+ end
129
+ end
130
+
131
+ # Resume a partially downloaded file
132
+ def resume
133
+ # See if file is writeable
134
+ if !File.writable?(@info.joined_filename)
135
+ @info.status = ON_DCC_ERROR
136
+ self.fire_event(ON_DCC_ERROR, $cfg.text.network.dcc.err_file_permission)
137
+ return
138
+ end
139
+ # As of now, no md5 checksum will be calculated for resumed packs in order to not
140
+ # slow down everything only due to md5 checksum calculation for the received data
141
+ @info.bytes_received = File.size(@info.joined_filename)
142
+ # Initialize verification
143
+ @info.verification.verified = false
144
+ @info.verification.bytes_mine = (@info.bytes_received > $cfg.network.dcc.verification_bytes) ? $cfg.network.dcc.verification_bytes : @info.bytes_received
145
+ if @info.bytes_received < @info.bytes_total
146
+ # Ask for DCC RESUME
147
+ @info.irc_user.irc_server.connect_to_events(IRC_Server::ON_USER_MESSAGE, self)
148
+ @info.irc_user.send("\001DCC RESUME #{@info.filename} #{@info.address.port} #{@info.bytes_received - @info.verification.bytes_mine}\001")
149
+ elsif @info.bytes_received == @info.bytes_total
150
+ # Pack already received. At least filesize tells us so. We therefore download a single byte from bot in
151
+ # order to terminate the connection fast and make it available for other packs.
152
+ # This is not hack, well to be honest it is, but it is necessary due to dcc protocol limitations.
153
+ @info.verification.verified = true
154
+ @info.skip_download = true
155
+ @info.skip_status = ON_DCC_COMPLETED
156
+ @info.skip_reason = ""
157
+ @tcp_con.open(@info.address.address.to_s, @info.address.port)
158
+ else
159
+ @info.verification.verified = true
160
+ @info.skip_download = true
161
+ @info.skip_status = ON_DCC_ERROR
162
+ @info.skip_reason = $cfg.text.network.dcc.err_filesize_differ
163
+ @tcp_con.open(@info.address.address.to_s, @info.address.port)
164
+ end
165
+ end
166
+
167
+ # Close current transfer if active
168
+ def close
169
+ if @info
170
+ @info.status = ON_DCC_ERROR
171
+ begin
172
+ @tcp_con.close if @tcp_con.connected?
173
+ @info.file.close if @info.file && !@info.file.closed?
174
+ rescue Exception
175
+ end
176
+ end
177
+ end
178
+
179
+ def on_event(caller, event, eventargs)
180
+ case event
181
+ when IRC_Server::ON_USER_MESSAGE
182
+ return if @info.status == ON_DCC_ERROR
183
+ irc_user, message, targets, irc_msg = eventargs
184
+ # Check for private bot message
185
+ irc_server = @info.irc_user.irc_server
186
+ if ((@info.irc_user != irc_user) || !irc_server.me.has_name?(targets[0]))
187
+ return
188
+ end
189
+ if irc_msg.ctcp_command?
190
+ if dcc = $cfg.network.dcc.accept_regexp.match(message)
191
+ irc_server.disconnect_from_events(IRC_Server::ON_USER_MESSAGE, self)
192
+ # DCC Resume available, connect
193
+ @tcp_con.open(@info.address.address.to_s, @info.address.port)
194
+ end
195
+ end
196
+
197
+ when TCP_Connection::ON_CONNECTED
198
+ if !@info.skip_download
199
+ # Open file (use binary mode windows platforms), append if file exists
200
+ begin
201
+ @info.file = File.new(@info.joined_filename, "a+b")
202
+ rescue Errno::EACCESS
203
+ @info.status = ON_DCC_ERROR
204
+ self.fire_event(ON_DCC_ERROR, $cfg.text.network.dcc.err_file_permission)
205
+ @tcp_con.close
206
+ return
207
+ end
208
+ if !@info.verification.verified
209
+ @info.file.seek(-@info.verification.bytes_mine, IO::SEEK_END)
210
+ @info.verification.md5_mine = Digest::MD5.new
211
+ @info.verification.md5_their = Digest::MD5.new
212
+ @info.verification.md5_mine << @info.file.read
213
+ else
214
+ @info.file.seek(0, IO::SEEK_END)
215
+ end
216
+ # Read verification bytes_mine
217
+ @info.file.sync = true
218
+ # Set internal stati and trigger event
219
+ @info.progress_timestamp = Time.now
220
+ @info.progress_bytes = 0
221
+ @info.status = ON_DCC_ESTABLISHED
222
+ self.fire_event(ON_DCC_ESTABLISHED)
223
+ else
224
+ if @info.skip_status == ON_DCC_COMPLETED
225
+ @info.status = ON_DCC_COMPLETED
226
+ self.fire_event(ON_DCC_COMPLETED, nil)
227
+ else
228
+ @info.status = ON_DCC_ERROR
229
+ self.fire_event(ON_DCC_ERROR, @info.skip_reason)
230
+ end
231
+ @tcp_con.close
232
+ end
233
+
234
+ when TCP_Connection::ON_CLOSED
235
+ if @info.bytes_received != @info.bytes_total
236
+ if !@info.status == ON_DCC_ERROR
237
+ # No other code has yet triggered a download failed event
238
+ @info.status = ON_DCC_ERROR
239
+ self.fire_event(ON_DCC_ERROR, $cfg.text.network.xdcc.err_con_closed)
240
+ end
241
+ else
242
+ @tcp_con.close if @tcp_con.connected?
243
+ @info.status = ON_DCC_COMPLETED
244
+ self.fire_event(ON_DCC_COMPLETED, @info.md5)
245
+ end
246
+
247
+ when TCP_Connection::ON_ERROR
248
+ # See if some other code has already triggered a download failed
249
+ if @info.status != ON_DCC_ERROR && @info.status != ON_DCC_COMPLETED
250
+ @info.status = ON_DCC_ERROR
251
+ self.fire_event(ON_DCC_ERROR, eventargs[0])
252
+ end
253
+
254
+ when TCP_Connection::ON_DATA
255
+ if (@info.skip_download || @info.file.closed?)
256
+ return
257
+ end
258
+
259
+ # Verify bytes if necessary
260
+ if !@info.verification.verified
261
+ self.add_verification_bytes(eventargs[0])
262
+ if @info.verification.bytes_mine == @info.verification.bytes_their
263
+ # Ready for md5 comparison
264
+ if @info.verification.md5_mine.hexdigest != @info.verification.md5_their.hexdigest
265
+ @info.status = ON_DCC_ERROR
266
+ self.fire_event(ON_DCC_ERROR, $cfg.text.network.dcc.err_verification)
267
+ @tcp_con.close
268
+ @info.file.close
269
+ return
270
+ else
271
+ @info.verification.verified = true
272
+ # Write remaining bytes to file
273
+ end
274
+ else
275
+ # All bytes have been eat up by verification
276
+ return
277
+ end
278
+ end
279
+
280
+ # Increment download count after possible verification, as @info.bytes_received
281
+ # starts counting at local filesize.
282
+ @info.bytes_received += eventargs[0].length
283
+ # Ack data, as 4 byte integer big-endian network byte order
284
+ @tcp_con.send([@info.bytes_received].pack('N'))
285
+
286
+ # Write to file
287
+ @info.file.syswrite(eventargs[0])
288
+ # Update MD5 if necessary
289
+ @info.md5 << eventargs[0] if @info.md5
290
+ # Update progress if necessary
291
+ @info.progress_bytes += eventargs[0].length
292
+ update_now = Time.now
293
+ if (time_elapsed = update_now - @info.progress_timestamp) >= 5
294
+ self.fire_event(
295
+ ON_DCC_PROGRESS,
296
+ @info.bytes_received.to_f / @info.bytes_total * 100, # Percent done
297
+ @info.bytes_received, # Total number of received bytes
298
+ (@info.progress_bytes.to_f / time_elapsed) / 1024) # Speed
299
+ @info.progress_bytes = 0
300
+ @info.progress_timestamp = update_now
301
+ end
302
+ if @info.bytes_received == @info.bytes_total
303
+ # Finished downloading
304
+ @info.file.close if (@info.file && !@info.file.closed?)
305
+ # Wait for the bot closing the connection
306
+ end
307
+
308
+ end
309
+ end
310
+
311
+ def add_verification_bytes(bytes)
312
+ return if @info.verification.bytes_their == @info.verification.bytes_mine
313
+ their_length = bytes.length
314
+ if (@info.verification.bytes_their + their_length <= @info.verification.bytes_mine)
315
+ @info.verification.bytes_their += their_length
316
+ @info.verification.md5_their << bytes.slice!(0..-1)
317
+ else
318
+ req_length = @info.verification.bytes_mine - @info.verification.bytes_their
319
+ @info.verification.bytes_their += req_length
320
+ @info.verification.md5_their << bytes.slice!(0..req_length - 1)
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,71 @@
1
+ # Copyright (c) 2005, Christoph Heindl
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without modification,
5
+ # are permitted provided that the following conditions are met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright notice, this list
8
+ # of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above copyright notice, this list
10
+ # of conditions and the following disclaimer in the documentation and/or other materials
11
+ # provided with the distribution.
12
+ # * Neither the name of Christoph Heindl nor the names of its contributors may be used to
13
+ # endorse or promote products derived from this software without specific prior written
14
+ # permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
17
+ # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18
+ # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
23
+ # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
+
25
+ require 'src/Network/IPAddr_Ext'
26
+
27
+ DCCAddress_Info = Struct.new("DCCAddress_Info", :address, :port)
28
+ DCCSEND_Info = Struct.new("DCCSEND_Info", :irc_user, :filename, :filesize, :dccaddress_info)
29
+
30
+ # Listens for incoming dcc sends and forwards
31
+ # them to its listeners
32
+ class DCC_Parser
33
+ include Event_Publisher
34
+
35
+ # -- Connectable events
36
+
37
+ # User is offering a file. Args: DCCSEND_Info
38
+ ON_DCC_SEND = Event_Publisher.next_global_eventID
39
+
40
+ def initialize(irc_server)
41
+ @irc_server = irc_server
42
+ @irc_server.connect_to_events(IRC_Server::ON_USER_MESSAGE, self)
43
+ self.init_events
44
+ end
45
+
46
+ def on_event(caller, eventtype, eventargs)
47
+ case(eventtype)
48
+ when IRC_Server::ON_USER_MESSAGE
49
+ irc_user, message, targets, irc_msg = eventargs
50
+ if irc_msg.ctcp_command? && @irc_server.me.has_name?(targets[0])
51
+ # See if it is a dcc send
52
+ if dcc = $cfg.network.dcc.send_regexp.match(message)
53
+ # User is offerng a file
54
+ # Try converting the given address info
55
+ begin
56
+ ipaddy = IPAddr.new_ntoh([dcc[2].to_i].pack("N"))
57
+ rescue Exception
58
+ ipaddy = nil
59
+ end
60
+ addr = DCCAddress_Info.new(ipaddy, dcc[3].to_i)
61
+ # Sanity check filename for invalid characters
62
+ filename = dcc[1].gsub(/\/{1,}|\\{1,}|\.{2,}/, "")
63
+ # Build DCCSEND_Info
64
+ info = DCCSEND_Info.new(irc_user, filename, dcc[4].to_i, addr)
65
+ self.fire_event(ON_DCC_SEND, info)
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,76 @@
1
+ # Copyright (c) 2005, Christoph Heindl
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without modification,
5
+ # are permitted provided that the following conditions are met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright notice, this list
8
+ # of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above copyright notice, this list
10
+ # of conditions and the following disclaimer in the documentation and/or other materials
11
+ # provided with the distribution.
12
+ # * Neither the name of Christoph Heindl nor the names of its contributors may be used to
13
+ # endorse or promote products derived from this software without specific prior written
14
+ # permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
17
+ # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18
+ # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
23
+ # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
+
25
+ require 'ipaddr'
26
+
27
+ # Extends the IPAddr class with ip address range tests.
28
+ class IPAddr
29
+
30
+ # Split ip address at each dot
31
+ def split_at_dot(ip)
32
+ a = Array.new
33
+ ip.split(".") do |part|
34
+ a << part
35
+ end
36
+ end
37
+
38
+ # Determine if the ip address is within the specified ip address range
39
+ # Example: "192.168.x.x" evals true for 192.168.0.0 to 192.168.255.255
40
+ # Example: "10.0.10-20.x" evals true for 10.0.10.0 to 10.0.19.255
41
+ def in_range?(ip_range)
42
+ ret = true
43
+ ip_parts = self.split_at_dot(self.to_s)
44
+ range_parts = self.split_at_dot(ip_range)
45
+ range_parts.each_with_index do |part, i|
46
+ if (reg = part.match(/^\d+$/))
47
+ # Is single ip address
48
+ if reg[0] != ip_parts[i]
49
+ ret = false
50
+ break
51
+ end
52
+ elsif reg = part.match(/^(\d+)-(\d+)$/)
53
+ ip_part = ip_parts[i].to_i
54
+ if ip_part < reg[1].to_i || ip_part >= reg[2].to_i
55
+ ret = false
56
+ break
57
+ end
58
+ end
59
+ end
60
+ ret
61
+ end
62
+
63
+ # Determines if current ip address is routable over the internet or not
64
+ def routable_address?
65
+ return !self.in_range?("10.x.x.x") &&
66
+ !self.in_range?("172.16-32.x.x") &&
67
+ !self.in_range?("192.168.x.x") &&
68
+ !self.local_loopback?
69
+ end
70
+
71
+ # Determines if current ip address is representing a local loopback ip address
72
+ def local_loopback?
73
+ return self.in_range?("127.x.x.x")
74
+ end
75
+ end
76
+