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.
- data/COPYING +23 -0
- data/XDCC-Fetch.rbw +54 -0
- data/doc/ark.png +0 -0
- data/doc/connect_established.png +0 -0
- data/doc/index.html +300 -0
- data/doc/mega_ark.png +0 -0
- data/doc/package.png +0 -0
- data/doc/package_unknown.png +0 -0
- data/doc/shot1.png +0 -0
- data/doc/shot1_mini.png +0 -0
- data/doc/shot2.png +0 -0
- data/doc/shot2_mini.png +0 -0
- data/doc/xdccfetch.css +155 -0
- data/icons/ark.png +0 -0
- data/icons/ark_big.png +0 -0
- data/icons/camera_test.png +0 -0
- data/icons/cancel.png +0 -0
- data/icons/connect_creating.png +0 -0
- data/icons/connect_established.png +0 -0
- data/icons/connect_failed.png +0 -0
- data/icons/connect_no.png +0 -0
- data/icons/edit_add.png +0 -0
- data/icons/edit_remove.png +0 -0
- data/icons/exit.png +0 -0
- data/icons/fileclose.png +0 -0
- data/icons/folder_inbox.png +0 -0
- data/icons/idea.png +0 -0
- data/icons/mega_ark.png +0 -0
- data/icons/messagebox_critical.png +0 -0
- data/icons/messagebox_info.png +0 -0
- data/icons/messagebox_warning.png +0 -0
- data/icons/messagebox_warning_small.png +0 -0
- data/icons/package.png +0 -0
- data/icons/package_favourite.png +0 -0
- data/icons/package_unknown.png +0 -0
- data/src/Console/Console_Parser.rb +71 -0
- data/src/Console/XDCC_Pack_Match_Template.rb +29 -0
- data/src/Console/xdcc-fetch.rb +123 -0
- data/src/GUI/About_Dialog.rb +50 -0
- data/src/GUI/Application_Builder.rb +280 -0
- data/src/GUI/Context_Menu.rb +81 -0
- data/src/GUI/Custom_Tabs.rb +60 -0
- data/src/GUI/Dialog_Box.rb +116 -0
- data/src/GUI/Download_Finished_Box.rb +41 -0
- data/src/GUI/Empty_Text_Field_Handler.rb +86 -0
- data/src/GUI/Gui_Logic.rb +629 -0
- data/src/GUI/Icon_Loader.rb +58 -0
- data/src/GUI/Main_Window.rb +227 -0
- data/src/GUI/Packet_Item.rb +171 -0
- data/src/GUI/Packet_List.rb +145 -0
- data/src/GUI/Speed_Widget.rb +101 -0
- data/src/GUI/Talk_Back.rb +118 -0
- data/src/GUI/Toggle_Button.rb +56 -0
- data/src/Network/CTCP_Handler.rb +61 -0
- data/src/Network/DCC_File.rb +323 -0
- data/src/Network/DCC_Parser.rb +71 -0
- data/src/Network/IPAddr_Ext.rb +76 -0
- data/src/Network/IRC_Message.rb +161 -0
- data/src/Network/IRC_Server.rb +273 -0
- data/src/Network/IRC_Server_Respond_Map.rb +223 -0
- data/src/Network/IRC_User.rb +58 -0
- data/src/Network/TCP_Connection.rb +168 -0
- data/src/Network/XDCC_Announcement.rb +120 -0
- data/src/Network/XDCC_Announcement_Storage.rb +167 -0
- data/src/Network/XDCC_Download_Handler.rb +412 -0
- data/src/Network/XDCC_Pack.rb +58 -0
- data/src/Network/XDCC_Parser.rb +253 -0
- data/src/Translations/README +61 -0
- data/src/Translations/check_translations +83 -0
- data/src/Translations/de.rb +140 -0
- data/src/Translations/en.rb +145 -0
- data/src/Utilities/Configuration.rb +91 -0
- data/src/Utilities/Events.rb +87 -0
- data/src/Utilities/Globals.rb +138 -0
- data/src/Utilities/PrettyException.rb +1091 -0
- data/src/Utilities/Recursive_Open_Struct.rb +159 -0
- data/src/Utilities/Timer.rb +71 -0
- 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
|
+
|