XDCC-Fetch 1.386

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.
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
+