fargo 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/fargo/connection/base.rb +29 -36
- data/lib/fargo/connection/download.rb +69 -58
- data/lib/fargo/publisher.rb +22 -6
- data/lib/fargo/supports/downloads.rb +46 -22
- data/lib/fargo/supports/file_list.rb +26 -14
- data/lib/fargo/supports/persistence.rb +11 -8
- data/lib/fargo/version.rb +1 -1
- metadata +19 -15
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'active_support/configurable'
|
2
2
|
require 'active_support/callbacks'
|
3
3
|
|
4
|
-
module Fargo
|
4
|
+
module Fargo
|
5
5
|
class ConnectionError < RuntimeError; end
|
6
6
|
|
7
7
|
module Connection
|
@@ -10,16 +10,16 @@ module Fargo
|
|
10
10
|
include ActiveSupport::Configurable
|
11
11
|
include ActiveSupport::Callbacks
|
12
12
|
include Fargo::Publisher
|
13
|
-
|
13
|
+
|
14
14
|
attr_accessor :socket
|
15
15
|
define_callbacks :listen
|
16
|
-
|
16
|
+
|
17
17
|
def initialize client
|
18
18
|
@outgoing = Queue.new
|
19
19
|
@client = client
|
20
20
|
config.quit_on_disconnect = true
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def connect
|
24
24
|
Fargo.logger.info(
|
25
25
|
"#{self}: Opening connection with #{config.address}, #{config.port}"
|
@@ -35,7 +35,7 @@ module Fargo
|
|
35
35
|
def receive
|
36
36
|
raise 'Implement me!'
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def open_socket
|
40
40
|
@socket ||= TCPSocket.open config.address, config.port
|
41
41
|
rescue Errno::ECONNREFUSED
|
@@ -43,20 +43,21 @@ module Fargo
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def connected?
|
46
|
-
!@socket.nil?
|
46
|
+
!@socket.nil? && !@socket.closed?
|
47
47
|
end
|
48
48
|
|
49
49
|
def listen
|
50
50
|
return unless @threads.nil? || @threads.size == 0
|
51
|
-
|
51
|
+
|
52
52
|
run_callbacks :listen do
|
53
53
|
@threads = []
|
54
|
+
@looping = true
|
54
55
|
|
55
56
|
# Start a thread to read the socket
|
56
|
-
@threads << Thread.start {
|
57
|
+
@threads << Thread.start { read_data while @looping }
|
57
58
|
|
58
59
|
# Start a thread to send information from the queue
|
59
|
-
@threads << Thread.start {
|
60
|
+
@threads << Thread.start { write_data @outgoing.pop while @looping }
|
60
61
|
|
61
62
|
@threads.each { |t| t.abort_on_exception = true }
|
62
63
|
end
|
@@ -67,8 +68,10 @@ module Fargo
|
|
67
68
|
|
68
69
|
write "$Quit #{@client.config.nick}" if config.quit_on_disconnect
|
69
70
|
|
71
|
+
@looping = false
|
72
|
+
|
70
73
|
if @threads
|
71
|
-
@threads.each
|
74
|
+
@threads.each{ |t| t.exit unless t == Thread.current }
|
72
75
|
@threads.clear
|
73
76
|
end
|
74
77
|
|
@@ -86,48 +89,38 @@ module Fargo
|
|
86
89
|
connection_type = self.class.name.split('::').last.downcase
|
87
90
|
@client.publish :"#{connection_type}_disconnected"
|
88
91
|
end
|
89
|
-
|
92
|
+
|
90
93
|
def write string
|
91
94
|
string << '|' unless string.end_with?('|')
|
92
95
|
@outgoing << string # append this to the queue of things to be written
|
93
96
|
true
|
94
97
|
end
|
95
|
-
|
98
|
+
|
96
99
|
private
|
97
100
|
|
98
101
|
def read_data
|
99
|
-
|
100
|
-
|
102
|
+
data = @socket.gets '|'
|
103
|
+
raise ConnectionError.new('Received nil data!') if data.nil?
|
104
|
+
|
105
|
+
Fargo.logger.debug "#{self} Received: #{data.inspect}"
|
106
|
+
receive data.chomp('|')
|
107
|
+
rescue => e
|
108
|
+
unless @socket.closed?
|
109
|
+
Fargo.logger.warn "#{self}: Error reading data, going away: #{e}"
|
101
110
|
disconnect
|
102
|
-
else
|
103
|
-
begin
|
104
|
-
data = @socket.gets '|'
|
105
|
-
raise ConnectionError.new("Received nil data!") if data.nil?
|
106
|
-
rescue => e
|
107
|
-
Fargo.logger.warn "#{self}: Error reading data, disconnecting: #{e}"
|
108
|
-
disconnect
|
109
|
-
end
|
110
|
-
|
111
|
-
Fargo.logger.debug "#{self} Received: #{data.inspect}"
|
112
|
-
receive data.chomp('|')
|
113
111
|
end
|
114
112
|
end
|
115
113
|
|
116
114
|
def write_data data
|
117
|
-
|
118
|
-
|
115
|
+
Fargo.logger.debug "#{self} Sending: #{data.inspect}"
|
116
|
+
@socket << data
|
117
|
+
rescue => e
|
118
|
+
unless @socket.closed?
|
119
|
+
Fargo.logger.warn "#{self}: Error writing data, going away: #{e}"
|
119
120
|
disconnect
|
120
|
-
else
|
121
|
-
begin
|
122
|
-
Fargo.logger.debug "#{self} Sending: #{data.inspect}"
|
123
|
-
@socket << data
|
124
|
-
rescue
|
125
|
-
@client.publish :write_error
|
126
|
-
disconnect
|
127
|
-
end
|
128
121
|
end
|
129
122
|
end
|
130
|
-
|
123
|
+
|
131
124
|
end
|
132
125
|
end
|
133
126
|
end
|
@@ -3,24 +3,24 @@ require 'zlib'
|
|
3
3
|
module Fargo
|
4
4
|
module Connection
|
5
5
|
class Download < Base
|
6
|
-
|
6
|
+
|
7
7
|
include Fargo::Utils
|
8
8
|
include Fargo::Parser
|
9
|
-
|
9
|
+
|
10
10
|
set_callback :listen, :before, :pre_listen
|
11
11
|
set_callback :listen, :after do |connection|
|
12
12
|
send_lock if connection.config.first
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
attr_accessor :download
|
16
16
|
|
17
17
|
def pre_listen
|
18
18
|
Fargo.logger.debug "Initiating connection on: #{config.address}:#{config.port}"
|
19
|
-
|
19
|
+
|
20
20
|
config.quit_on_disconnect = false
|
21
21
|
@lock, @pk = generate_lock
|
22
22
|
@handshake_step = 0
|
23
|
-
|
23
|
+
|
24
24
|
@buffer_size = (2 << 12).freeze
|
25
25
|
end
|
26
26
|
|
@@ -30,32 +30,38 @@ module Fargo
|
|
30
30
|
|
31
31
|
def read_data
|
32
32
|
# only download if we're at the correct time
|
33
|
-
return super if @handshake_step != 6
|
33
|
+
return super if @handshake_step != 6
|
34
34
|
|
35
35
|
@exit_time = 20 # reset our timeout time
|
36
|
-
|
36
|
+
|
37
37
|
data = @socket.readpartial @buffer_size
|
38
|
-
|
38
|
+
|
39
39
|
if @zlib
|
40
40
|
@zs = Zlib::Inflate.new if @zs.nil?
|
41
41
|
data = @zs.inflate data
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
@file << data
|
45
45
|
@recvd += data.length
|
46
46
|
|
47
|
-
if @recvd
|
48
|
-
download_finished!
|
49
|
-
elsif @recvd > @length
|
47
|
+
if @recvd > @length
|
50
48
|
error "#{self} #{@recvd} > #{@length}!!!"
|
51
49
|
download_finished!
|
52
50
|
else
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
51
|
+
percent = @recvd.to_f / @length
|
52
|
+
if percent - @last_published > 0.05
|
53
|
+
@file.flush
|
54
|
+
publish :download_progress, :percent => percent,
|
55
|
+
:file => download_path,
|
56
|
+
:nick => @other_nick,
|
57
|
+
:download => @download,
|
58
|
+
:size => @recvd,
|
59
|
+
:compressed => @zlib
|
60
|
+
|
61
|
+
@last_published = percent
|
62
|
+
end
|
63
|
+
|
64
|
+
download_finished! if @recvd == @length
|
59
65
|
end
|
60
66
|
rescue IOError => e
|
61
67
|
error "#{self}: IOError, disconnecting #{e}"
|
@@ -67,7 +73,7 @@ module Fargo
|
|
67
73
|
case message[:type]
|
68
74
|
when :mynick
|
69
75
|
if @handshake_step == 0
|
70
|
-
@handshake_step = 1
|
76
|
+
@handshake_step = 1
|
71
77
|
@other_nick = message[:nick]
|
72
78
|
|
73
79
|
@client.connected_with! @other_nick
|
@@ -94,7 +100,7 @@ module Fargo
|
|
94
100
|
else
|
95
101
|
error 'Premature disconnect when lock received'
|
96
102
|
end
|
97
|
-
|
103
|
+
|
98
104
|
when :supports
|
99
105
|
if @handshake_step == 2
|
100
106
|
@client_extensions = message[:extensions]
|
@@ -102,7 +108,7 @@ module Fargo
|
|
102
108
|
else
|
103
109
|
error 'Premature disconnect when supports received'
|
104
110
|
end
|
105
|
-
|
111
|
+
|
106
112
|
when :direction
|
107
113
|
if @handshake_step == 3 && message[:direction] == 'upload'
|
108
114
|
@client_num = message[:number]
|
@@ -110,7 +116,7 @@ module Fargo
|
|
110
116
|
else
|
111
117
|
error 'Premature disconnect when direction received'
|
112
118
|
end
|
113
|
-
|
119
|
+
|
114
120
|
when :key
|
115
121
|
if @handshake_step == 4 && generate_key(@lock) == message[:key]
|
116
122
|
|
@@ -132,9 +138,9 @@ module Fargo
|
|
132
138
|
|
133
139
|
write "$Send" unless @client_extensions.include? 'ADCGet'
|
134
140
|
|
135
|
-
publish :download_started, :file => download_path,
|
136
|
-
:download => @download,
|
137
|
-
:nick => @other_nick
|
141
|
+
publish :download_started, :file => download_path,
|
142
|
+
:download => @download,
|
143
|
+
:nick => @other_nick
|
138
144
|
else
|
139
145
|
error "Premature disconnect when #{message[:type]} received"
|
140
146
|
end
|
@@ -142,27 +148,29 @@ module Fargo
|
|
142
148
|
when :noslots
|
143
149
|
if @download
|
144
150
|
Fargo.logger.debug "#{self}: No Slots for #{self[:download]}"
|
145
|
-
|
151
|
+
|
146
152
|
download_failed! 'No Slots'
|
147
153
|
end
|
148
154
|
|
149
155
|
when :error
|
150
|
-
|
151
|
-
|
152
|
-
|
156
|
+
Fargo.logger.warn @last_error = "#{self}: Error! #{message[:message]}"
|
157
|
+
download_failed! message[:message]
|
158
|
+
|
159
|
+
# This wasn't handled by us, proxy it on up to the client
|
153
160
|
else
|
154
161
|
@client.publish message[:type], message
|
155
162
|
|
156
163
|
end
|
157
164
|
end
|
158
|
-
|
165
|
+
|
159
166
|
def begin_download!
|
160
|
-
@file = File.
|
167
|
+
@file = File.open download_path, 'wb'
|
161
168
|
|
162
169
|
@file.seek @download.offset
|
163
170
|
@file.sync = true
|
164
171
|
@socket.sync = true
|
165
172
|
@handshake_step = 5
|
173
|
+
@last_published = 0
|
166
174
|
|
167
175
|
if @download.file_list?
|
168
176
|
if @client_extensions.include? 'XmlBZList'
|
@@ -177,7 +185,7 @@ module Fargo
|
|
177
185
|
if @client_extensions.include? 'ADCGet'
|
178
186
|
download_query = @download.file
|
179
187
|
if @download.tth && @client_extensions.include?('TTHF')
|
180
|
-
download_query = @download.tth
|
188
|
+
download_query = 'TTH/' + @download.tth
|
181
189
|
end
|
182
190
|
|
183
191
|
zlig = ''
|
@@ -194,66 +202,67 @@ module Fargo
|
|
194
202
|
# This is the thread for the timeout of a connection. The @exit_time
|
195
203
|
# variable is reset to 20 after every bit of information is received.
|
196
204
|
@exit_time = 20
|
197
|
-
@exit_thread = Thread.start {
|
205
|
+
@exit_thread = Thread.start {
|
198
206
|
while @exit_time > 0
|
199
207
|
sleep 1
|
200
208
|
@exit_time -= 1
|
201
209
|
Fargo.logger.debug "#{self} time out in #{@exit_time} seconds"
|
202
210
|
end
|
203
211
|
|
204
|
-
download_failed! 'Download timeout!'
|
212
|
+
download_failed! 'Download timeout!'
|
205
213
|
}
|
206
|
-
|
214
|
+
|
207
215
|
Fargo.logger.debug "#{self}: Beginning download of #{@download}"
|
208
216
|
end
|
209
|
-
|
217
|
+
|
210
218
|
def download_failed! msg, opts = {}
|
211
219
|
Fargo.logger.debug "#{self}: #{msg} #{@download}"
|
212
|
-
|
220
|
+
|
213
221
|
# cache because publishing must be at end of method and we're about to
|
214
222
|
# clear these
|
215
223
|
path, download = download_path, @download
|
216
224
|
|
217
225
|
reset_download
|
218
226
|
|
219
|
-
publish :download_failed, opts.merge(:nick => @other_nick,
|
220
|
-
:download => download,
|
221
|
-
:file => path,
|
227
|
+
publish :download_failed, opts.merge(:nick => @other_nick,
|
228
|
+
:download => download,
|
229
|
+
:file => path,
|
222
230
|
:last_error => msg)
|
223
231
|
|
224
232
|
@exit_thread = nil
|
225
233
|
end
|
226
|
-
|
234
|
+
|
227
235
|
def download_finished!
|
228
236
|
Fargo.logger.debug "#{self}: Finished download of #{@download}"
|
229
|
-
|
237
|
+
|
230
238
|
# cache because publishing must be at end of method and we're about to
|
231
239
|
# clear these
|
232
240
|
path, download = download_path, @download
|
233
|
-
|
241
|
+
|
234
242
|
reset_download
|
235
|
-
|
236
|
-
publish :download_finished, :file => path, :download => download,
|
243
|
+
|
244
|
+
publish :download_finished, :file => path, :download => download,
|
237
245
|
:nick => @other_nick
|
246
|
+
disconnect if download.file_list?
|
238
247
|
end
|
239
|
-
|
248
|
+
|
240
249
|
def disconnect
|
241
250
|
Fargo.logger.debug "#{self} Disconnecting from: #{@other_nick}"
|
242
|
-
|
251
|
+
|
243
252
|
super
|
244
253
|
|
245
254
|
if @download
|
246
255
|
download_failed! @last_error, :recvd => @recvd, :length => @length
|
247
256
|
end
|
248
|
-
|
257
|
+
|
249
258
|
reset_download
|
250
259
|
end
|
251
|
-
|
260
|
+
|
252
261
|
private
|
253
262
|
def reset_download
|
254
263
|
@file.close unless @file.nil? || @file.closed?
|
255
264
|
if @file_path && File.exists?(@file_path) && File.size(@file_path) == 0
|
256
|
-
File.delete(@file_path)
|
265
|
+
File.delete(@file_path)
|
257
266
|
end
|
258
267
|
|
259
268
|
if @socket
|
@@ -262,18 +271,18 @@ module Fargo
|
|
262
271
|
end
|
263
272
|
|
264
273
|
# If this was called from exit thread, don't kill it
|
265
|
-
if @exit_thread != Thread.current
|
274
|
+
if @exit_thread != Thread.current
|
266
275
|
@exit_thread.exit if @exit_thread && @exit_thread.alive?
|
267
276
|
@exit_thread = nil
|
268
277
|
end
|
269
|
-
|
278
|
+
|
270
279
|
# clear out these variables
|
271
|
-
@zs = @file_path = @zlib = @download = @length = @recvd = nil
|
280
|
+
@zs = @file_path = @zlib = @download = @length = @recvd = nil
|
272
281
|
|
273
282
|
# Go back to the get step
|
274
283
|
@handshake_step = 5
|
275
284
|
end
|
276
|
-
|
285
|
+
|
277
286
|
def download_path
|
278
287
|
return nil if @download.try(:file).nil?
|
279
288
|
|
@@ -282,10 +291,12 @@ module Fargo
|
|
282
291
|
filename = File.basename @download.file.gsub("\\", '/')
|
283
292
|
path = File.join(prefix, @other_nick, filename)
|
284
293
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
294
|
+
unless @download.file_list?
|
295
|
+
i = 0
|
296
|
+
while File.exists?(path)
|
297
|
+
i += 1
|
298
|
+
path = File.join(prefix, @other_nick, "#{i}-#{filename}")
|
299
|
+
end
|
289
300
|
end
|
290
301
|
|
291
302
|
path
|
data/lib/fargo/publisher.rb
CHANGED
@@ -1,26 +1,42 @@
|
|
1
1
|
module Fargo
|
2
2
|
module Publisher
|
3
|
-
|
3
|
+
|
4
4
|
attr_reader :subscribers
|
5
5
|
|
6
6
|
def subscribe &subscriber
|
7
|
-
raise RuntimeError.new(
|
7
|
+
raise RuntimeError.new('Need a subscription block!') if subscriber.nil?
|
8
|
+
|
8
9
|
Fargo.logger.debug "#{self}: subscribing #{subscriber}"
|
9
10
|
(@subscribers ||= []) << subscriber
|
10
11
|
end
|
11
|
-
|
12
|
+
|
12
13
|
def subscribed_to?
|
13
14
|
@subscribers && @subscribers.size > 0
|
14
15
|
end
|
15
|
-
|
16
|
+
|
16
17
|
def unsubscribe &subscriber
|
17
|
-
raise RuntimeError.new(
|
18
|
+
raise RuntimeError.new('Need a subscription block!') if subscriber.nil?
|
19
|
+
|
18
20
|
Fargo.logger.debug "#{self}: unsubscribing #{subscriber}"
|
19
21
|
(@subscribers ||= []).delete subscriber
|
20
22
|
end
|
21
23
|
|
22
24
|
def publish message_type, hash = {}
|
23
|
-
@subscribers
|
25
|
+
@subscribers ||= []
|
26
|
+
|
27
|
+
to_remove = []
|
28
|
+
|
29
|
+
@subscribers.each do |subscriber|
|
30
|
+
begin
|
31
|
+
subscriber.call message_type, hash
|
32
|
+
rescue => e
|
33
|
+
Fargo.logger.warn "#{self}: error publishing to: #{subscriber}! #{e}"
|
34
|
+
to_remove << subscriber
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
@subscribers -= to_remove
|
24
39
|
end
|
40
|
+
|
25
41
|
end
|
26
42
|
end
|
@@ -10,31 +10,48 @@ module Fargo
|
|
10
10
|
file == 'files.xml.bz2'
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
attr_reader :current_downloads, :finished_downloads, :queued_downloads,
|
15
15
|
:failed_downloads, :open_download_slots, :trying, :timed_out,
|
16
16
|
:download_slots
|
17
|
-
|
17
|
+
|
18
18
|
included do
|
19
19
|
set_callback :setup, :after, :initialize_queues
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def clear_failed_downloads
|
23
23
|
failed_downloads.clear
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def clear_finished_downloads
|
27
27
|
finished_downloads.clear
|
28
28
|
end
|
29
29
|
|
30
|
-
def download nick, file, tth=nil, size=-1, offset=0
|
30
|
+
def download nick, file=nil, tth=nil, size=-1, offset=0
|
31
31
|
raise ConnectionException.new 'Not connected yet!' unless hub
|
32
|
-
raise 'File cannot be nil!' if file.nil?
|
33
32
|
|
33
|
+
if nick.is_a?(Supports::FileList::Listing)
|
34
|
+
listing = nick
|
35
|
+
nick = listing.nick
|
36
|
+
file = listing.name
|
37
|
+
tth = listing.tth
|
38
|
+
size = listing.size
|
39
|
+
elsif nick.is_a?(Download)
|
40
|
+
dl = nick
|
41
|
+
nick = dl.nick
|
42
|
+
file = dl.file
|
43
|
+
tth = dl.tth
|
44
|
+
size = dl.size || -1
|
45
|
+
offset = dl.offset || 0
|
46
|
+
end
|
47
|
+
|
48
|
+
raise 'File must not be nil!' if file.nil?
|
34
49
|
unless nicks.include? nick
|
35
|
-
raise ConnectionException.new "User #{nick} does not exist!"
|
50
|
+
raise ConnectionException.new "User #{nick} does not exist!"
|
36
51
|
end
|
37
52
|
|
53
|
+
tth = tth.gsub /^TTH:/, '' if tth
|
54
|
+
|
38
55
|
download = Download.new nick, file, tth, size, offset
|
39
56
|
download.percent = 0
|
40
57
|
download.status = 'idle'
|
@@ -52,13 +69,13 @@ module Fargo
|
|
52
69
|
Fargo.logger.warn "#{file} isn't a failed download for: #{nick}!"
|
53
70
|
return
|
54
71
|
end
|
55
|
-
|
72
|
+
|
56
73
|
@failed_downloads[nick].delete dl
|
57
74
|
download dl.nick, dl.file, dl.tth, dl.size
|
58
75
|
end
|
59
76
|
|
60
77
|
def remove_download nick, file
|
61
|
-
# We need to synchronize this access, so append these arguments to a
|
78
|
+
# We need to synchronize this access, so append these arguments to a
|
62
79
|
# queue to be processed later
|
63
80
|
@to_remove << [nick, file]
|
64
81
|
true
|
@@ -93,11 +110,11 @@ module Fargo
|
|
93
110
|
@downloading_lock.synchronize {
|
94
111
|
# Find the first nick and download list
|
95
112
|
arr = @queued_downloads.to_a.detect{ |nick, downloads|
|
96
|
-
downloads.size > 0 &&
|
97
|
-
!@current_downloads.has_key?(nick) &&
|
98
|
-
!@trying.include?(nick) &&
|
99
|
-
!@timed_out.include?(nick) &&
|
100
|
-
has_slot?(nick)
|
113
|
+
downloads.size > 0 &&
|
114
|
+
!@current_downloads.has_key?(nick) &&
|
115
|
+
!@trying.include?(nick) &&
|
116
|
+
!@timed_out.include?(nick) &&
|
117
|
+
(connection_for(nick) || has_slot?(nick))
|
101
118
|
}
|
102
119
|
|
103
120
|
return false if arr.nil? || arr.size == 0
|
@@ -131,7 +148,7 @@ module Fargo
|
|
131
148
|
return nil
|
132
149
|
end
|
133
150
|
|
134
|
-
download = @queued_downloads[user].shift
|
151
|
+
download = @queued_downloads[user].shift
|
135
152
|
@current_downloads[user] = download
|
136
153
|
@trying.delete user
|
137
154
|
|
@@ -160,12 +177,14 @@ module Fargo
|
|
160
177
|
|
161
178
|
download
|
162
179
|
end
|
163
|
-
|
180
|
+
|
164
181
|
def download_finished! user, failed
|
165
182
|
download = nil
|
166
|
-
@downloading_lock.synchronize{
|
183
|
+
@downloading_lock.synchronize{
|
167
184
|
download = @current_downloads.delete user
|
168
185
|
@open_download_slots += 1
|
186
|
+
|
187
|
+
# connection_for(user).disconnect if @queued_downloads[user].size == 0
|
169
188
|
}
|
170
189
|
|
171
190
|
if failed
|
@@ -176,7 +195,7 @@ module Fargo
|
|
176
195
|
|
177
196
|
start_download # Start another download if possible
|
178
197
|
end
|
179
|
-
|
198
|
+
|
180
199
|
def connection_failed_with! nick
|
181
200
|
@trying.delete nick
|
182
201
|
@timed_out << nick
|
@@ -224,7 +243,7 @@ module Fargo
|
|
224
243
|
@download_removal_thread.exit
|
225
244
|
end
|
226
245
|
|
227
|
-
# Both of these need access to the synchronization lock, so we use
|
246
|
+
# Both of these need access to the synchronization lock, so we use
|
228
247
|
# separate threads to do these processes.
|
229
248
|
def start_download_queue_threads
|
230
249
|
@to_download = Queue.new
|
@@ -236,8 +255,13 @@ module Fargo
|
|
236
255
|
download.status = 'timeout'
|
237
256
|
(@failed_downloads[download.nick] ||= []) << download
|
238
257
|
else
|
239
|
-
|
240
|
-
|
258
|
+
@queued_downloads[download.nick] ||= []
|
259
|
+
|
260
|
+
unless @queued_downloads[download.nick].include?(download) ||
|
261
|
+
@current_downloads[download.nick] == download
|
262
|
+
@queued_downloads[download.nick] << download
|
263
|
+
start_download
|
264
|
+
end
|
241
265
|
end
|
242
266
|
}
|
243
267
|
}
|
@@ -256,6 +280,6 @@ module Fargo
|
|
256
280
|
}
|
257
281
|
end
|
258
282
|
|
259
|
-
end # Downloads
|
283
|
+
end # Downloads
|
260
284
|
end # Supports
|
261
285
|
end # Fargo
|
@@ -4,13 +4,19 @@ require 'libxml'
|
|
4
4
|
module Fargo
|
5
5
|
module Supports
|
6
6
|
module FileList
|
7
|
-
class Listing < Struct.new(:tth, :size, :name); end
|
7
|
+
class Listing < Struct.new(:tth, :size, :name, :nick); end
|
8
8
|
|
9
9
|
# Lazily load the file list for the nick. Subscribe to the client for the
|
10
10
|
# event :file_list to get notified.
|
11
11
|
def file_list nick
|
12
12
|
@file_list ||= {}
|
13
|
-
|
13
|
+
@getting_file_list ||= {}
|
14
|
+
|
15
|
+
if @file_list.has_key?(nick)
|
16
|
+
return parse_file_list(@file_list[nick], nick)
|
17
|
+
elsif @getting_file_list[nick]
|
18
|
+
return true
|
19
|
+
end
|
14
20
|
|
15
21
|
file_gotten = lambda{ |type, map|
|
16
22
|
case type
|
@@ -19,19 +25,23 @@ module Fargo
|
|
19
25
|
@file_list[nick] = map[:file]
|
20
26
|
unsubscribe &file_gotten
|
21
27
|
publish :file_list, :nick => nick, :list => @file_list[nick]
|
28
|
+
@getting_file_list.delete nick
|
22
29
|
end
|
23
30
|
end
|
24
31
|
}
|
25
32
|
|
26
33
|
subscribe &file_gotten
|
27
34
|
|
35
|
+
@getting_file_list[nick] = true
|
28
36
|
download nick, 'files.xml.bz2'
|
29
37
|
end
|
30
38
|
|
31
39
|
# Wait for the results to arrive, timed out after some time
|
32
40
|
def file_list! nick, timeout = 10
|
33
41
|
@file_list ||= {}
|
34
|
-
|
42
|
+
if @file_list.has_key?(nick)
|
43
|
+
return parse_file_list(@file_list[nick], nick)
|
44
|
+
end
|
35
45
|
|
36
46
|
list = nil
|
37
47
|
list_gotten = lambda{ |type, map|
|
@@ -45,35 +55,37 @@ module Fargo
|
|
45
55
|
|
46
56
|
timeout_response(timeout, list_gotten){ file_list nick }
|
47
57
|
|
48
|
-
parse_file_list list
|
58
|
+
parse_file_list list, nick
|
49
59
|
end
|
50
60
|
|
51
61
|
private
|
52
62
|
|
53
|
-
def parse_file_list file
|
63
|
+
def parse_file_list file, nick
|
54
64
|
if file && File.exists?(file)
|
65
|
+
Fargo.logger.debug "Parsing file list for: '#{nick}' at '#{file}'"
|
55
66
|
xml = Bzip2::Reader.open(file).read
|
56
67
|
doc = LibXML::XML::Document.string xml
|
57
68
|
|
58
|
-
construct_file_list doc.root
|
69
|
+
construct_file_list doc.root, nil, nick
|
59
70
|
else
|
60
71
|
nil
|
61
72
|
end
|
62
73
|
end
|
63
74
|
|
64
|
-
def construct_file_list node
|
75
|
+
def construct_file_list node, prefix, nick
|
65
76
|
list = {}
|
66
77
|
|
67
78
|
node.each_element do |element|
|
68
|
-
|
79
|
+
path = prefix ? prefix + "\\" + element['Name'] : element['Name']
|
80
|
+
|
69
81
|
if element.name =~ /directory/i
|
70
|
-
list[
|
82
|
+
list[element['Name']] = construct_file_list element, path, nick
|
71
83
|
else
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
84
|
+
# Why does this consistently segfault ruby 1.8.7 when I convert
|
85
|
+
# element['Size'] to an integer before the struct is created?!
|
86
|
+
element = list[element['Name']] = Listing.new(element['TTH'],
|
87
|
+
element['Size'], path, nick)
|
88
|
+
element.size = element.size.to_i
|
77
89
|
end
|
78
90
|
end
|
79
91
|
|
@@ -13,30 +13,33 @@ module Fargo
|
|
13
13
|
|
14
14
|
def connection_for nick
|
15
15
|
c = @connection_cache.try :[], nick
|
16
|
-
|
16
|
+
if c.nil? || c.connected?
|
17
|
+
Fargo.logger.debug "#{self} has connection with: #{nick}: #{c}"
|
18
|
+
return c
|
19
|
+
end
|
17
20
|
|
18
21
|
# If it's present and not connected, remove it from the cache
|
19
22
|
@connection_cache.try :delete, nick
|
20
23
|
nil
|
21
24
|
end
|
22
|
-
|
25
|
+
|
23
26
|
def connected_with? nick
|
24
27
|
c = @connection_cache.try :[], nick
|
25
28
|
c.connected? unless c.nil?
|
26
|
-
end
|
27
|
-
|
29
|
+
end
|
30
|
+
|
28
31
|
def disconnect_from nick
|
29
32
|
c = @connection_cache.try :delete, nick
|
30
33
|
c.disconnect unless c.nil?
|
31
34
|
end
|
32
|
-
|
35
|
+
|
33
36
|
def nicks_connected_with
|
34
37
|
return [] if @connection_cache.nil?
|
35
38
|
|
36
39
|
nicks = @connection_cache.keys
|
37
|
-
nicks.reject{ |n| !connected_with? n }
|
40
|
+
nicks.reject{ |n| !connected_with? n }
|
38
41
|
end
|
39
|
-
|
42
|
+
|
40
43
|
def setup_connection_cache
|
41
44
|
@connection_cache = {}
|
42
45
|
|
@@ -49,4 +52,4 @@ module Fargo
|
|
49
52
|
|
50
53
|
end
|
51
54
|
end
|
52
|
-
end
|
55
|
+
end
|
data/lib/fargo/version.rb
CHANGED
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fargo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Alex Crichton
|
@@ -14,51 +15,54 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-09-
|
18
|
+
date: 2010-09-22 00:00:00 -04:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
|
-
|
22
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
23
|
none: false
|
24
24
|
requirements:
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
+
hash: 7
|
27
28
|
segments:
|
28
29
|
- 3
|
29
30
|
- 0
|
30
31
|
- 0
|
31
32
|
version: 3.0.0
|
33
|
+
requirement: *id001
|
32
34
|
type: :runtime
|
35
|
+
name: activesupport
|
33
36
|
prerelease: false
|
34
|
-
version_requirements: *id001
|
35
37
|
- !ruby/object:Gem::Dependency
|
36
|
-
|
37
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
38
39
|
none: false
|
39
40
|
requirements:
|
40
41
|
- - ">="
|
41
42
|
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
42
44
|
segments:
|
43
45
|
- 0
|
44
46
|
version: "0"
|
47
|
+
requirement: *id002
|
45
48
|
type: :runtime
|
49
|
+
name: libxml-ruby
|
46
50
|
prerelease: false
|
47
|
-
version_requirements: *id002
|
48
51
|
- !ruby/object:Gem::Dependency
|
49
|
-
|
50
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
51
53
|
none: false
|
52
54
|
requirements:
|
53
55
|
- - ">="
|
54
56
|
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
55
58
|
segments:
|
56
59
|
- 0
|
57
60
|
version: "0"
|
61
|
+
requirement: *id003
|
58
62
|
type: :runtime
|
63
|
+
name: bzip2-ruby
|
59
64
|
prerelease: false
|
60
|
-
|
61
|
-
description: DC Client
|
65
|
+
description: Direct Connect (DC) Client implemented in pure Ruby
|
62
66
|
email: alex@alexcrichton.com
|
63
67
|
executables: []
|
64
68
|
|
@@ -103,7 +107,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
107
|
requirements:
|
104
108
|
- - ">="
|
105
109
|
- !ruby/object:Gem::Version
|
106
|
-
hash:
|
110
|
+
hash: 3
|
107
111
|
segments:
|
108
112
|
- 0
|
109
113
|
version: "0"
|
@@ -112,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
116
|
requirements:
|
113
117
|
- - ">="
|
114
118
|
- !ruby/object:Gem::Version
|
115
|
-
hash:
|
119
|
+
hash: 3
|
116
120
|
segments:
|
117
121
|
- 0
|
118
122
|
version: "0"
|