fargo 0.1.0 → 0.1.1
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/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"
|