fargo 0.2.0 → 0.3.0
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/README.md +61 -0
- data/ext/fargo/extconf.rb +1 -1
- data/ext/fargo/fargo_tth.c +0 -2
- data/lib/fargo/client.rb +30 -24
- data/lib/fargo/parser.rb +90 -59
- data/lib/fargo/protocol/dc.rb +24 -8
- data/lib/fargo/protocol/hub.rb +41 -20
- data/lib/fargo/protocol/peer.rb +114 -0
- data/lib/fargo/protocol/{download.rb → peer_download.rb} +51 -115
- data/lib/fargo/protocol/peer_upload.rb +160 -0
- data/lib/fargo/search.rb +38 -5
- data/lib/fargo/supports/downloads.rb +17 -17
- data/lib/fargo/supports/local_file_list.rb +164 -0
- data/lib/fargo/supports/nick_list.rb +18 -5
- data/lib/fargo/supports/persistence.rb +2 -2
- data/lib/fargo/supports/{file_list.rb → remote_file_list.rb} +4 -5
- data/lib/fargo/supports/searches.rb +5 -12
- data/lib/fargo/supports/uploads.rb +24 -5
- data/lib/fargo/version.rb +1 -1
- data/lib/fargo.rb +12 -3
- data/spec/fargo/parser_spec.rb +198 -0
- data/spec/fargo/protocol/dc_spec.rb +77 -0
- data/spec/fargo/protocol/hub_spec.rb +155 -0
- data/spec/fargo/protocol/peer_download_spec.rb +104 -0
- data/spec/fargo/protocol/peer_spec.rb +58 -0
- data/spec/fargo/protocol/peer_upload_spec.rb +128 -0
- data/spec/fargo/search_spec.rb +107 -0
- data/spec/fargo/supports/chat_spec.rb +33 -0
- data/spec/fargo/supports/local_file_list_spec.rb +70 -0
- data/spec/fargo/supports/nick_list_spec.rb +35 -0
- data/spec/fargo/utils_spec.rb +26 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/support/matchers.rb +17 -0
- metadata +37 -9
- data/lib/fargo/search_result.rb +0 -31
@@ -1,32 +1,12 @@
|
|
1
|
-
require 'zlib'
|
2
|
-
|
3
1
|
module Fargo
|
4
2
|
module Protocol
|
5
|
-
|
6
|
-
|
7
|
-
include Fargo::Utils
|
8
|
-
include Fargo::Protocol::DC
|
9
|
-
|
10
|
-
attr_accessor :download, :client
|
11
|
-
|
12
|
-
def post_init
|
13
|
-
super
|
3
|
+
module PeerDownload
|
14
4
|
|
15
|
-
|
16
|
-
|
17
|
-
@lock, @pk = generate_lock
|
18
|
-
@handshake_step = 0
|
19
|
-
end
|
20
|
-
|
21
|
-
def send_lock
|
22
|
-
@lock_sent = true
|
23
|
-
send_message 'MyNick', @client.config.nick
|
24
|
-
send_message 'Lock', "#{@lock} Pk=#{@pk}"
|
25
|
-
end
|
5
|
+
attr_accessor :download
|
26
6
|
|
27
|
-
def
|
7
|
+
def receive_data_chunk data
|
28
8
|
# only download if we're at the correct handshake step
|
29
|
-
return super if @handshake_step != 6
|
9
|
+
return super if @handshake_step != 6 || @download.nil?
|
30
10
|
|
31
11
|
if @zlib
|
32
12
|
@inflator = Zlib::Inflate.new if @inflator.nil?
|
@@ -55,76 +35,30 @@ module Fargo
|
|
55
35
|
|
56
36
|
download_finished! if @recvd == @length
|
57
37
|
end
|
38
|
+
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_data?
|
43
|
+
@handshake_step != 6
|
58
44
|
end
|
59
45
|
|
60
46
|
def receive_message type, message
|
61
47
|
case type
|
62
|
-
when :
|
63
|
-
if @handshake_step == 0
|
64
|
-
@handshake_step = 1
|
65
|
-
@other_nick = message[:nick]
|
66
|
-
|
67
|
-
client.channel << [:download_opened,
|
68
|
-
publish_args.merge(:connection => self)]
|
69
|
-
@download = @client.lock_next_download! @other_nick, self
|
70
|
-
|
71
|
-
if @download.try(:file).nil?
|
72
|
-
error "Nothing to download from:#{@other_nick}!"
|
73
|
-
end
|
74
|
-
else
|
75
|
-
error 'Premature disconnect when mynick received'
|
76
|
-
end
|
77
|
-
|
78
|
-
when :lock
|
79
|
-
if @handshake_step == 1
|
80
|
-
@remote_lock = message[:lock]
|
81
|
-
@handshake_step = 2
|
82
|
-
|
83
|
-
send_lock unless @lock_sent
|
84
|
-
|
85
|
-
send_message 'Supports', 'TTHF ADCGet ZLIG'
|
86
|
-
send_message 'Direction', "Download #{@my_num = rand(10000)}"
|
87
|
-
send_message 'Key', generate_key(@remote_lock)
|
88
|
-
else
|
89
|
-
error 'Premature disconnect when lock received'
|
90
|
-
end
|
91
|
-
|
92
|
-
when :supports
|
93
|
-
if @handshake_step == 2
|
94
|
-
@client_extensions = message[:extensions]
|
95
|
-
@handshake_step = 3
|
96
|
-
else
|
97
|
-
error 'Premature disconnect when supports received'
|
98
|
-
end
|
99
|
-
|
100
|
-
when :direction
|
101
|
-
if @handshake_step == 3 && message[:direction] == 'upload'
|
102
|
-
@client_num = message[:number]
|
103
|
-
@handshake_step = 4
|
104
|
-
else
|
105
|
-
error 'Premature disconnect when direction received'
|
106
|
-
end
|
107
|
-
|
108
|
-
when :key
|
109
|
-
if @handshake_step == 4 && generate_key(@lock) == message[:key]
|
110
|
-
|
111
|
-
FileUtils.mkdir_p File.dirname(download_path), :mode => 0755
|
112
|
-
|
113
|
-
begin_download!
|
114
|
-
|
115
|
-
else
|
116
|
-
error 'Premature disconnect when key received'
|
117
|
-
end
|
118
|
-
|
119
|
-
when :file_length, :adcsnd
|
48
|
+
when :file_length, :adcsnd, :sending
|
120
49
|
if @handshake_step == 5
|
121
50
|
@recvd = 0
|
122
51
|
@handshake_step = 6
|
123
52
|
|
124
|
-
@zlib = message[:zlib]
|
53
|
+
@zlib = message[:zlib] unless @getblock_sent
|
125
54
|
@length = message[:size]
|
126
55
|
|
127
|
-
send_message 'Send'
|
56
|
+
send_message 'Send' if @get_sent
|
57
|
+
|
58
|
+
if @zlib
|
59
|
+
Fargo.logger.debug(
|
60
|
+
"Enabling zlib compression on: #{@download.file}")
|
61
|
+
end
|
128
62
|
|
129
63
|
@client.channel << [:download_started, {:file => download_path,
|
130
64
|
:download => @download,
|
@@ -147,11 +81,11 @@ module Fargo
|
|
147
81
|
# This wasn't handled by us, proxy it on up to the client
|
148
82
|
else
|
149
83
|
super
|
150
|
-
|
151
84
|
end
|
152
85
|
end
|
153
86
|
|
154
87
|
def begin_download!
|
88
|
+
FileUtils.mkdir_p File.dirname(download_path), :mode => 0755
|
155
89
|
@file = File.open download_path, 'wb'
|
156
90
|
|
157
91
|
@file.seek @download.offset
|
@@ -174,20 +108,45 @@ module Fargo
|
|
174
108
|
download_query = 'TTH/' + @download.tth
|
175
109
|
end
|
176
110
|
|
177
|
-
zlig = ''
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
111
|
+
zlig = @client_extensions.include?('ZLIG') ? ' ZL1' : ''
|
112
|
+
|
113
|
+
send_message 'ADCGET', "file #{download_query} #{@download.offset} #{@download.size}#{zlig}"
|
114
|
+
|
115
|
+
# See http://www.teamfair.info/wiki/index.php?title=XmlBZList for
|
116
|
+
# what the $Supports extensions mean for the U?GetZ?Block commands
|
117
|
+
elsif @client_extensions.include? 'GetZBlock'
|
118
|
+
@getblock_sent = true
|
119
|
+
@zlib = true
|
120
|
+
send_message 'UGetZBlock',
|
121
|
+
"#{@download.offset} #{@download.size} #{@download.file}"
|
122
|
+
elsif @client_extensions.include? 'XmlBZList'
|
123
|
+
@getblock_sent = true
|
124
|
+
@zlib = false
|
125
|
+
send_message 'UGetBlock',
|
126
|
+
"#{@download.offset} #{@download.size} #{@download.file}"
|
182
127
|
|
183
|
-
send_message 'ADCGET', "file #{download_query} #{@download.offset} #{@download.size} #{zlig}"
|
184
128
|
else
|
129
|
+
@get_sent = true
|
185
130
|
send_message 'Get', "#{@download.file}$#{@download.offset + 1}"
|
186
131
|
end
|
187
132
|
|
188
133
|
Fargo.logger.debug "#{self}: Beginning download of #{@download}"
|
189
134
|
end
|
190
135
|
|
136
|
+
def unbind
|
137
|
+
super
|
138
|
+
|
139
|
+
Fargo.logger.debug "#{self} Disconnected from: #{@other_nick}"
|
140
|
+
|
141
|
+
if @download
|
142
|
+
download_failed! @last_error, :recvd => @recvd, :length => @length
|
143
|
+
end
|
144
|
+
|
145
|
+
reset_download
|
146
|
+
end
|
147
|
+
|
148
|
+
protected
|
149
|
+
|
191
150
|
def download_failed! msg, opts = {}
|
192
151
|
Fargo.logger.debug "#{self}: #{msg} #{@download}"
|
193
152
|
|
@@ -218,24 +177,6 @@ module Fargo
|
|
218
177
|
close_connection_after_writing if download.file_list?
|
219
178
|
end
|
220
179
|
|
221
|
-
def publish_args
|
222
|
-
{:nick => @other_nick}
|
223
|
-
end
|
224
|
-
|
225
|
-
def unbind
|
226
|
-
super
|
227
|
-
|
228
|
-
Fargo.logger.debug "#{self} Disconnected from: #{@other_nick}"
|
229
|
-
|
230
|
-
if @download
|
231
|
-
download_failed! @last_error, :recvd => @recvd, :length => @length
|
232
|
-
end
|
233
|
-
|
234
|
-
reset_download
|
235
|
-
end
|
236
|
-
|
237
|
-
private
|
238
|
-
|
239
180
|
def reset_download
|
240
181
|
@file.close unless @file.nil? || @file.closed?
|
241
182
|
|
@@ -245,6 +186,7 @@ module Fargo
|
|
245
186
|
|
246
187
|
# clear out these variables
|
247
188
|
@inflator = @file_path = @zlib = @download = @length = @recvd = nil
|
189
|
+
@get_sent = @getblock_sent = false
|
248
190
|
|
249
191
|
# Go back to the get step
|
250
192
|
@handshake_step = 5
|
@@ -270,12 +212,6 @@ module Fargo
|
|
270
212
|
end
|
271
213
|
end
|
272
214
|
|
273
|
-
def error message
|
274
|
-
Fargo.logger.warn @last_error = message
|
275
|
-
|
276
|
-
close_connection
|
277
|
-
end
|
278
|
-
|
279
215
|
end
|
280
216
|
end
|
281
217
|
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Fargo
|
2
|
+
module Protocol
|
3
|
+
module PeerUpload
|
4
|
+
|
5
|
+
CHUNKSIZE = 16 * 1024
|
6
|
+
|
7
|
+
def receive_message type, message
|
8
|
+
case type
|
9
|
+
when :adcget, :getblock, :get
|
10
|
+
if @handshake_step == 5
|
11
|
+
if message[:file] == 'files.xml.bz2'
|
12
|
+
@listing = 'filelist'
|
13
|
+
else
|
14
|
+
@listing = @client.listing_for message[:file].gsub("\\", '/')
|
15
|
+
end
|
16
|
+
|
17
|
+
if message[:size] == -1
|
18
|
+
if @listing == 'filelist'
|
19
|
+
@size = File.size @client.local_file_list_path
|
20
|
+
else
|
21
|
+
@size = @listing.try :size
|
22
|
+
end
|
23
|
+
else
|
24
|
+
@size = message[:size]
|
25
|
+
end
|
26
|
+
|
27
|
+
@offset = message[:offset]
|
28
|
+
@zlib = message[:zlib]
|
29
|
+
|
30
|
+
if @listing.nil?
|
31
|
+
if type == :getblock
|
32
|
+
send_message 'Failed', 'File Not Available'
|
33
|
+
else
|
34
|
+
send_message 'Error', 'File Not Available'
|
35
|
+
end
|
36
|
+
elsif @client.open_upload_slots == 0 && @listing != 'filelist'
|
37
|
+
send_message 'MaxedOut'
|
38
|
+
elsif type == :adcget
|
39
|
+
zl = @zlib ? ' ZL1' : ''
|
40
|
+
send_message 'ADCSND',
|
41
|
+
"#{message[:kind]} #{message[:file]} #{@offset} #{@size}#{zl}"
|
42
|
+
|
43
|
+
begin_streaming
|
44
|
+
elsif type == :getblock
|
45
|
+
if message[:size] == -1
|
46
|
+
send_message 'Sending'
|
47
|
+
else
|
48
|
+
send_message 'Sending', @size
|
49
|
+
end
|
50
|
+
|
51
|
+
begin_streaming
|
52
|
+
else
|
53
|
+
@handshake_step = 10
|
54
|
+
|
55
|
+
send_message 'FileLength', @size
|
56
|
+
end
|
57
|
+
else
|
58
|
+
error "Premature disconnect when #{type} received"
|
59
|
+
end
|
60
|
+
|
61
|
+
when :send
|
62
|
+
if @handshake_step == 10
|
63
|
+
begin_streaming
|
64
|
+
else
|
65
|
+
error "Premature disconnect when #{type} received"
|
66
|
+
end
|
67
|
+
|
68
|
+
when :cancel
|
69
|
+
if @handshake_step == 11
|
70
|
+
cancel_streaming
|
71
|
+
else
|
72
|
+
error "Premature disconnect when cancel received"
|
73
|
+
end
|
74
|
+
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def unbind
|
81
|
+
super
|
82
|
+
|
83
|
+
if @listing
|
84
|
+
Fargo.logger.debug "Upload disconnected"
|
85
|
+
finish_streaming
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
def begin_streaming
|
92
|
+
@handshake_step = 11
|
93
|
+
|
94
|
+
if @listing == 'filelist'
|
95
|
+
@file = File.open @client.local_file_list_path, 'rb'
|
96
|
+
else
|
97
|
+
@client.take_slot!
|
98
|
+
@file = File.open File.join(@listing.root, @listing.name), 'rb'
|
99
|
+
end
|
100
|
+
|
101
|
+
@file.seek @offset
|
102
|
+
@deflator = Zlib::Deflate.new if @zlib
|
103
|
+
@sent = 0
|
104
|
+
@looping = true
|
105
|
+
|
106
|
+
stream_file
|
107
|
+
end
|
108
|
+
|
109
|
+
def finish_streaming
|
110
|
+
@file.try :close
|
111
|
+
|
112
|
+
if @listing == 'filelist'
|
113
|
+
close_connection_after_writing
|
114
|
+
else
|
115
|
+
@client.release_slot!
|
116
|
+
end
|
117
|
+
|
118
|
+
@deflator = @file = @sent = @size = @offset = @listing = @zlib = nil
|
119
|
+
@looping = @canceled = false
|
120
|
+
@handshake_step = 5
|
121
|
+
end
|
122
|
+
|
123
|
+
def cancel_streaming
|
124
|
+
@looping = false
|
125
|
+
@canceled = true
|
126
|
+
end
|
127
|
+
|
128
|
+
def stream_file
|
129
|
+
while @looping do
|
130
|
+
if @sent < @size
|
131
|
+
if get_outbound_data_size > 4 * CHUNKSIZE
|
132
|
+
EventMachine.next_tick{ stream_file }
|
133
|
+
break
|
134
|
+
else
|
135
|
+
to_send = [CHUNKSIZE, @size - @sent].min
|
136
|
+
@sent += to_send
|
137
|
+
|
138
|
+
data = @file.read to_send
|
139
|
+
if @zlib
|
140
|
+
flush_flag = (@sent == @size ? Zlib::FINISH : Zlib::NO_FLUSH)
|
141
|
+
data = @deflator.deflate data, flush_flag
|
142
|
+
end
|
143
|
+
|
144
|
+
send_data data
|
145
|
+
end
|
146
|
+
else
|
147
|
+
finish_streaming
|
148
|
+
break
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
if @canceled
|
153
|
+
send_message 'Canceled'
|
154
|
+
finish_streaming
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/fargo/search.rb
CHANGED
@@ -6,8 +6,21 @@ module Fargo
|
|
6
6
|
COMPRESSED = 3
|
7
7
|
DOCUMENT = 4
|
8
8
|
EXECUTABLE = 5
|
9
|
+
PICTURE = 6
|
9
10
|
VIDEO = 7
|
10
11
|
FOLDER = 8
|
12
|
+
TTH = 9
|
13
|
+
|
14
|
+
# See http://www.teamfair.info/wiki/index.php?title=$Search for the
|
15
|
+
# extensions
|
16
|
+
EXTENSIONS = {
|
17
|
+
AUDIO => [/mp(2|3)/, 'wav', 'au', /(r|s)m/, 'mid', 'flac', 'm4a'],
|
18
|
+
COMPRESSED => ['zip', 'arj', 'rar', 'lzh', 'gz', 'z', 'arc', 'pak'],
|
19
|
+
DOCUMENT => [/docx?/, 'txt', 'wri', 'pdf', 'ps', 'tex'],
|
20
|
+
EXECUTABLE => ['pm', 'exe', 'bat', 'com'],
|
21
|
+
PICTURE => ['gif', /jpe?g/, 'bmp', 'pcx', 'png', 'wmf', 'psd'],
|
22
|
+
VIDEO => [/mpe?g/, 'avi', 'asf', 'mov', 'mkv']
|
23
|
+
}
|
11
24
|
|
12
25
|
attr_accessor :size_restricted, :is_minimum_size, :size, :filetype, :pattern
|
13
26
|
|
@@ -36,14 +49,34 @@ module Fargo
|
|
36
49
|
pattern.gsub('$', ' ')
|
37
50
|
end
|
38
51
|
|
39
|
-
def
|
40
|
-
|
52
|
+
def matches? map
|
53
|
+
if map.is_a?(Listing)
|
54
|
+
listing = map
|
55
|
+
map = {
|
56
|
+
:file => listing.name,
|
57
|
+
:size => listing.size,
|
58
|
+
:tth => listing.tth
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
file = map[:file].try(:downcase) || ''
|
41
63
|
|
42
|
-
|
43
|
-
|
64
|
+
if @filetype == TTH
|
65
|
+
matches_query = (@pattern =~ /^TTH:(\w+)$/ && map[:tth] == $1)
|
66
|
+
else
|
67
|
+
matches_query = queries.inject(true) do |last, word|
|
68
|
+
last && file.index(word.downcase)
|
69
|
+
end
|
70
|
+
|
71
|
+
patterns = EXTENSIONS[@filetype]
|
72
|
+
|
73
|
+
if patterns && matches_query
|
74
|
+
ext = File.extname file
|
75
|
+
matches_query = patterns.any?{ |p| ext =~ /^\.#{p}$/i }
|
76
|
+
end
|
44
77
|
end
|
45
78
|
|
46
|
-
if size_restricted
|
79
|
+
if size_restricted
|
47
80
|
if is_minimum_size
|
48
81
|
matches_query && map[:size] > size
|
49
82
|
else
|
@@ -1,16 +1,16 @@
|
|
1
1
|
module Fargo
|
2
|
+
class Download < Struct.new(:nick, :file, :tth, :size, :offset)
|
3
|
+
attr_accessor :percent, :status
|
4
|
+
|
5
|
+
def file_list?
|
6
|
+
file == 'files.xml.bz2'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
2
10
|
module Supports
|
3
11
|
module Downloads
|
4
12
|
extend ActiveSupport::Concern
|
5
13
|
|
6
|
-
class Download < Struct.new(:nick, :file, :tth, :size, :offset)
|
7
|
-
attr_accessor :percent, :status
|
8
|
-
|
9
|
-
def file_list?
|
10
|
-
file == 'files.xml.bz2'
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
14
|
included do
|
15
15
|
set_callback :initialization, :after, :initialize_download_lists
|
16
16
|
end
|
@@ -30,22 +30,22 @@ module Fargo
|
|
30
30
|
finished_downloads.clear
|
31
31
|
end
|
32
32
|
|
33
|
-
def download nick, file=nil, tth=nil, size
|
33
|
+
def download nick, file = nil, tth = nil, size = -1, offset = 0
|
34
34
|
raise ConnectionException.new 'Not connected yet!' unless hub
|
35
35
|
|
36
|
-
if nick.is_a?(
|
37
|
-
listing = nick
|
38
|
-
nick = listing.nick
|
39
|
-
file = listing.name
|
40
|
-
tth = listing.tth
|
41
|
-
size = listing.size
|
42
|
-
elsif nick.is_a?(Download)
|
36
|
+
if nick.is_a?(Download)
|
43
37
|
dl = nick
|
44
38
|
nick = dl.nick
|
45
39
|
file = dl.file
|
46
40
|
tth = dl.tth
|
47
41
|
size = dl.size || -1
|
48
42
|
offset = dl.offset || 0
|
43
|
+
elsif nick.is_a?(Listing) # i.e. a listing
|
44
|
+
listing = nick
|
45
|
+
nick = listing.nick
|
46
|
+
file = listing.name
|
47
|
+
tth = listing.tth
|
48
|
+
size = listing.size
|
49
49
|
end
|
50
50
|
|
51
51
|
raise 'File must not be nil!' if file.nil?
|
@@ -177,7 +177,7 @@ module Fargo
|
|
177
177
|
download.percent = 1
|
178
178
|
download.status = 'finished'
|
179
179
|
download_finished! user, false
|
180
|
-
elsif type == :download_failed || type == :
|
180
|
+
elsif type == :download_failed || type == :peer_disconnected
|
181
181
|
channel.unsubscribe subscribed_id
|
182
182
|
download.status = 'failed'
|
183
183
|
download_finished! user, true
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'bzip2'
|
2
|
+
require 'libxml'
|
3
|
+
require 'active_support/core_ext/module/synchronization'
|
4
|
+
|
5
|
+
module Fargo
|
6
|
+
module Supports
|
7
|
+
module LocalFileList
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
include TTH
|
10
|
+
|
11
|
+
attr_reader :local_file_list
|
12
|
+
|
13
|
+
included do
|
14
|
+
set_callback :initialization, :after, :initialize_upload_lists
|
15
|
+
set_callback :connect, :after, :schedule_update
|
16
|
+
end
|
17
|
+
|
18
|
+
def share_directory dir
|
19
|
+
@shared_directories << dir unless @shared_directories.include? dir
|
20
|
+
|
21
|
+
if connected?
|
22
|
+
EventMachine.defer {
|
23
|
+
update_tth dir
|
24
|
+
write_file_list
|
25
|
+
}
|
26
|
+
else
|
27
|
+
update_tth dir
|
28
|
+
write_file_list
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def share_size
|
33
|
+
config.override_share_size || @share_size
|
34
|
+
end
|
35
|
+
|
36
|
+
def local_file_list_path
|
37
|
+
File.join config.config_dir, 'files.xml.bz2'
|
38
|
+
end
|
39
|
+
|
40
|
+
def local_listings
|
41
|
+
collect_local_listings @local_file_list, [], nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def search_local_listings search
|
45
|
+
collect_local_listings @local_file_list, [], search
|
46
|
+
end
|
47
|
+
|
48
|
+
def listing_for query
|
49
|
+
if query =~ /^TTH\/(\w+)$/
|
50
|
+
tth = $1
|
51
|
+
local_listings.detect{ |l| l.tth = tth }
|
52
|
+
else
|
53
|
+
local_listings.detect{ |l| l.name == query }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def collect_local_listings hash, arr, search
|
60
|
+
hash.each_pair do |k, v|
|
61
|
+
if v.is_a?(Listing)
|
62
|
+
arr << v if search.nil? || search.matches?(v)
|
63
|
+
else
|
64
|
+
collect_local_listings v, arr, search
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
arr
|
69
|
+
end
|
70
|
+
|
71
|
+
def write_file_list
|
72
|
+
doc = LibXML::XML::Document.new
|
73
|
+
doc.root = LibXML::XML::Node.new 'FileListing'
|
74
|
+
doc.root['Version'] = '1'
|
75
|
+
doc.root['Base'] = '/'
|
76
|
+
doc.root['Generator'] = "fargo #{VERSION}"
|
77
|
+
|
78
|
+
create_entities @local_file_list, doc.root
|
79
|
+
|
80
|
+
FileUtils.mkdir_p config.config_dir
|
81
|
+
Bzip2::Writer.open(local_file_list_path, 'w') do |f|
|
82
|
+
f << doc.to_s(:indent => false)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def update_tth root, directory = nil, hash = nil
|
87
|
+
if directory.nil?
|
88
|
+
directory = root
|
89
|
+
root = File.dirname(root)
|
90
|
+
end
|
91
|
+
|
92
|
+
hash ||= (@local_file_list[File.basename(directory)] ||= {})
|
93
|
+
|
94
|
+
Pathname.glob(directory + '/*').each do |path|
|
95
|
+
if path.directory?
|
96
|
+
update_tth_without_synchronization root, path.to_s,
|
97
|
+
hash[path.basename.to_s] ||= {}
|
98
|
+
elsif hash[path.basename.to_s].nil? ||
|
99
|
+
path.mtime > hash[path.basename.to_s].mtime
|
100
|
+
hash[path.basename.to_s] = Listing.new(
|
101
|
+
file_tth(path.to_s),
|
102
|
+
path.size,
|
103
|
+
path.to_s.gsub(root + '/', ''),
|
104
|
+
config.nick,
|
105
|
+
path.mtime,
|
106
|
+
root
|
107
|
+
)
|
108
|
+
|
109
|
+
@share_size += path.size
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
to_remove = []
|
114
|
+
|
115
|
+
hash.each_pair do |k, v|
|
116
|
+
file = directory + '/' + k
|
117
|
+
unless File.exists?(file)
|
118
|
+
to_remove << k
|
119
|
+
@share_size -= File.size file if File.file?(file)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
to_remove.each{ |k| hash.delete k }
|
124
|
+
end
|
125
|
+
|
126
|
+
synchronize :update_tth, :with => :@update_lock
|
127
|
+
|
128
|
+
def create_entities entity, node
|
129
|
+
entity.each_pair do |k, v|
|
130
|
+
if v.is_a? Hash
|
131
|
+
dir = LibXML::XML::Node.new 'Directory'
|
132
|
+
dir['Name'] = k
|
133
|
+
create_entities v, dir
|
134
|
+
node << dir
|
135
|
+
else
|
136
|
+
file = LibXML::XML::Node.new 'File'
|
137
|
+
file['Name'] = k
|
138
|
+
file['Size'] = v.size.to_s
|
139
|
+
file['TTH'] = v.tth
|
140
|
+
|
141
|
+
node << file
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def schedule_update
|
147
|
+
EventMachine::Timer.new(60) do
|
148
|
+
@shared_directories.each{ |d| update_tth d }
|
149
|
+
|
150
|
+
write_file_list
|
151
|
+
schedule_update
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def initialize_upload_lists
|
156
|
+
@shared_directories = []
|
157
|
+
@local_file_list = {}
|
158
|
+
@share_size = 0
|
159
|
+
@update_lock = Mutex.new
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|