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,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/numeric/time'
|
2
|
+
|
1
3
|
module Fargo
|
2
4
|
module Supports
|
3
5
|
module NickList
|
@@ -33,9 +35,14 @@ module Fargo
|
|
33
35
|
@nick_info[nick]
|
34
36
|
end
|
35
37
|
|
38
|
+
# This query must be up to date so remove any cached information we have
|
39
|
+
# about the nick so we can get a fresh copy
|
36
40
|
def nick_has_slot? nick
|
37
|
-
|
38
|
-
|
41
|
+
if @search_result_slots[nick] &&
|
42
|
+
@search_result_slots[nick][:updated_at] + 10.minutes > Time.now
|
43
|
+
return @search_result_slots[nick][:slots] > 0
|
44
|
+
end
|
45
|
+
|
39
46
|
@nick_info.try :delete, nick
|
40
47
|
info = info nick
|
41
48
|
|
@@ -54,23 +61,29 @@ module Fargo
|
|
54
61
|
def initialize_nick_lists
|
55
62
|
@nicks = []
|
56
63
|
@nick_info = Hash.new{ |h, k| h[k] = {} }
|
64
|
+
@search_result_slots = {}
|
57
65
|
|
58
66
|
channel.subscribe do |type, map|
|
59
67
|
case type
|
60
68
|
when :hello
|
61
|
-
@nicks << map[:
|
69
|
+
@nicks << map[:nick] unless @nicks.include? map[:nick]
|
62
70
|
when :myinfo
|
63
71
|
@nick_info[map[:nick]] = map
|
64
72
|
when :nick_list
|
65
73
|
@nicks = map[:nicks]
|
66
74
|
when :quit
|
67
|
-
@nicks.delete map[:
|
68
|
-
@nick_info.delete map[:
|
75
|
+
@nicks.delete map[:nick]
|
76
|
+
@nick_info.delete map[:nick]
|
69
77
|
when :hub_disconnected
|
70
78
|
@nicks.clear
|
71
79
|
@nick_info.clear
|
72
80
|
when :userip
|
73
81
|
map[:users].each_pair{ |nick, ip| @nick_info[nick][:ip] = ip }
|
82
|
+
when :search_result
|
83
|
+
@search_result_slots[map[:nick]] = {
|
84
|
+
:slots => map[:open_slots],
|
85
|
+
:updated_at => Time.now
|
86
|
+
}
|
74
87
|
end
|
75
88
|
end
|
76
89
|
end
|
@@ -10,7 +10,7 @@ module Fargo
|
|
10
10
|
def connect_with nick
|
11
11
|
@connection_timeouts[nick] = EventMachine::Timer.new(10) do
|
12
12
|
@connection_timeouts.delete(nick)
|
13
|
-
channel
|
13
|
+
channel << [:connection_timeout, {:nick => nick}]
|
14
14
|
end
|
15
15
|
|
16
16
|
if config.passive
|
@@ -46,7 +46,7 @@ module Fargo
|
|
46
46
|
channel.subscribe do |type, hash|
|
47
47
|
if type == :hub_disconnected
|
48
48
|
nicks_connected_with.each{ |n| disconnect_from n }
|
49
|
-
elsif type == :
|
49
|
+
elsif type == :peer_disconnected
|
50
50
|
@connection_cache.delete hash[:nick]
|
51
51
|
elsif type == :download_opened
|
52
52
|
@connection_timeouts.delete(hash[:nick]).try(:cancel)
|
@@ -2,9 +2,10 @@ require 'bzip2'
|
|
2
2
|
require 'libxml'
|
3
3
|
|
4
4
|
module Fargo
|
5
|
+
class Listing < Struct.new(:tth, :size, :name, :nick, :mtime, :root); end
|
6
|
+
|
5
7
|
module Supports
|
6
|
-
module
|
7
|
-
class Listing < Struct.new(:tth, :size, :name, :nick); end
|
8
|
+
module RemoteFileList
|
8
9
|
|
9
10
|
extend ActiveSupport::Concern
|
10
11
|
|
@@ -61,7 +62,7 @@ module Fargo
|
|
61
62
|
parse_file_list list, nick
|
62
63
|
end
|
63
64
|
|
64
|
-
|
65
|
+
protected
|
65
66
|
|
66
67
|
def parse_file_list file, nick
|
67
68
|
if file && File.exists?(file)
|
@@ -95,8 +96,6 @@ module Fargo
|
|
95
96
|
list
|
96
97
|
end
|
97
98
|
|
98
|
-
protected
|
99
|
-
|
100
99
|
def initialize_file_lists
|
101
100
|
@file_list = {}
|
102
101
|
@getting_file_list = {}
|
@@ -7,15 +7,6 @@ module Fargo
|
|
7
7
|
set_callback :initialization, :after, :initialize_search_caches
|
8
8
|
end
|
9
9
|
|
10
|
-
# see parser#@@search for what's passed in
|
11
|
-
#
|
12
|
-
# searches this client's files based on those options and returns an array
|
13
|
-
# of SearchResult(s)
|
14
|
-
def search_files options
|
15
|
-
# TODO: implement me
|
16
|
-
[]
|
17
|
-
end
|
18
|
-
|
19
10
|
def search_hub query
|
20
11
|
raise ConnectionError.new('Not connected Yet!') unless connected?
|
21
12
|
|
@@ -55,8 +46,8 @@ module Fargo
|
|
55
46
|
protected
|
56
47
|
|
57
48
|
def normalize search
|
58
|
-
unless search.is_a?
|
59
|
-
search =
|
49
|
+
unless search.is_a? Search
|
50
|
+
search = Search.new :query => search
|
60
51
|
end
|
61
52
|
|
62
53
|
search
|
@@ -68,8 +59,10 @@ module Fargo
|
|
68
59
|
|
69
60
|
channel.subscribe do |type, map|
|
70
61
|
if type == :search_result
|
62
|
+
map[:tth] = map[:hub] if map[:hub] =~ /^TTH:/
|
63
|
+
|
71
64
|
@searches.keys.each do |search|
|
72
|
-
if @search_objects[search].
|
65
|
+
if @search_objects[search].matches?(map)
|
73
66
|
@searches[search] << map
|
74
67
|
end
|
75
68
|
end
|
@@ -1,14 +1,33 @@
|
|
1
|
+
require 'active_support/core_ext/module/synchronization'
|
2
|
+
|
1
3
|
module Fargo
|
2
4
|
module Supports
|
3
5
|
module Uploads
|
4
|
-
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
set_callback :initialization, :after, :initialize_upload_locks
|
10
|
+
end
|
11
|
+
|
12
|
+
def open_upload_slots
|
13
|
+
[config.upload_slots - @taken_slots, 0].max
|
14
|
+
end
|
5
15
|
|
6
|
-
def
|
7
|
-
|
16
|
+
def take_slot!
|
17
|
+
@taken_slots += 1
|
8
18
|
end
|
9
19
|
|
10
|
-
def
|
11
|
-
|
20
|
+
def release_slot!
|
21
|
+
@taken_slots -= 1
|
22
|
+
end
|
23
|
+
|
24
|
+
synchronize :take_slot!, :release_slot!, :with => :@upload_slot_lock
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def initialize_upload_locks
|
29
|
+
@upload_slot_lock = Mutex.new
|
30
|
+
@taken_slots = 0
|
12
31
|
end
|
13
32
|
|
14
33
|
end
|
data/lib/fargo/version.rb
CHANGED
data/lib/fargo.rb
CHANGED
@@ -2,6 +2,7 @@ require 'fileutils'
|
|
2
2
|
require 'eventmachine'
|
3
3
|
require 'active_support/dependencies/autoload'
|
4
4
|
require 'active_support/core_ext/module/attribute_accessors'
|
5
|
+
require 'active_support/core_ext/module/delegation'
|
5
6
|
require 'active_support/buffered_logger'
|
6
7
|
require 'active_support/concern'
|
7
8
|
require 'active_support/configurable'
|
@@ -11,7 +12,7 @@ module Fargo
|
|
11
12
|
|
12
13
|
class ConnectionException < RuntimeError; end
|
13
14
|
|
14
|
-
mattr_accessor:logger
|
15
|
+
mattr_accessor :logger
|
15
16
|
self.logger = ActiveSupport::BufferedLogger.new STDOUT
|
16
17
|
|
17
18
|
autoload :Utils
|
@@ -21,6 +22,8 @@ module Fargo
|
|
21
22
|
autoload :SearchResult
|
22
23
|
autoload :VERSION
|
23
24
|
autoload :TTH
|
25
|
+
autoload :Listing, 'fargo/supports/remote_file_list'
|
26
|
+
autoload :Download, 'fargo/supports/downloads'
|
24
27
|
|
25
28
|
module Supports
|
26
29
|
extend ActiveSupport::Autoload
|
@@ -32,15 +35,21 @@ module Fargo
|
|
32
35
|
autoload :Downloads
|
33
36
|
autoload :Persistence
|
34
37
|
autoload :Timeout
|
35
|
-
autoload :
|
38
|
+
autoload :RemoteFileList
|
39
|
+
autoload :LocalFileList
|
36
40
|
end
|
37
41
|
|
38
42
|
module Protocol
|
39
43
|
extend ActiveSupport::Autoload
|
40
44
|
|
41
45
|
autoload :DC
|
42
|
-
autoload :
|
46
|
+
autoload :Peer
|
47
|
+
autoload :PeerDownload
|
48
|
+
autoload :PeerUpload
|
43
49
|
autoload :Hub
|
44
50
|
end
|
45
51
|
|
52
|
+
class << self
|
53
|
+
delegate :config, :configure, :to => Client
|
54
|
+
end
|
46
55
|
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fargo::Parser do
|
4
|
+
|
5
|
+
let(:helper) { helper_object described_class }
|
6
|
+
|
7
|
+
it "correctly parses info strings" do
|
8
|
+
helper.parse_message(
|
9
|
+
"$MyINFO $ALL notdan <++ V:0.75,M:A,H:1/0/0,S:1,Dt:1.2.6/W>$ $Morewood A-D\001$$90932631814$"
|
10
|
+
).should == {
|
11
|
+
:type => :myinfo,
|
12
|
+
:nick => 'notdan',
|
13
|
+
:speed => 'Morewood A-D',
|
14
|
+
:email => '',
|
15
|
+
:sharesize => 90932631814,
|
16
|
+
:interest => '<++ V:0.75,M:A,H:1/0/0,S:1,Dt:1.2.6/W>'
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
it "correctly parses hello commands" do
|
21
|
+
helper.parse_message("$Hello notdan").should == {
|
22
|
+
:type => :hello,
|
23
|
+
:nick => 'notdan'
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "correctly parses the quit command" do
|
28
|
+
helper.parse_message("$Quit ghostafaria").should == {
|
29
|
+
:type => :quit,
|
30
|
+
:nick => 'ghostafaria'
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
it "correctly parses search commands" do
|
35
|
+
helper.parse_message(
|
36
|
+
"$Search 128.237.66.53:36565 F?T?0?1?west$wing").should == {
|
37
|
+
:type => :search,
|
38
|
+
:restrict_size => false,
|
39
|
+
:is_minimum_size => false,
|
40
|
+
:pattern => 'west$wing',
|
41
|
+
:filetype => 1,
|
42
|
+
:address => '128.237.66.53',
|
43
|
+
:port => 36565,
|
44
|
+
:size => 0
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'search results' do
|
49
|
+
context 'active searches' do
|
50
|
+
it "correctly parses search results which are files" do
|
51
|
+
helper.parse_message(
|
52
|
+
"$SR notas 02 - Heroes.mp3\0053001347 "+
|
53
|
+
"2/4\005TTH:VZWDTXHBSBSW7UMKCFHNEHXWCSWWYGU4KTIL76A " +
|
54
|
+
"(127.0.0.1:7314)").should == {
|
55
|
+
:type => :search_result,
|
56
|
+
:open_slots => 2,
|
57
|
+
:slots => 4,
|
58
|
+
:hub => 'TTH:VZWDTXHBSBSW7UMKCFHNEHXWCSWWYGU4KTIL76A',
|
59
|
+
:file => '02 - Heroes.mp3',
|
60
|
+
:address => '127.0.0.1',
|
61
|
+
:port => 7314,
|
62
|
+
:size => 3001347,
|
63
|
+
:nick => 'notas'
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
it "correctly parses search results which are directories" do
|
68
|
+
helper.parse_message(
|
69
|
+
"$SR notas Music 2/4\005" +
|
70
|
+
"TTH:EAAABGHTP4AAACAAAAAAAAAAACIOWCMZ6N7QAAA (127.0.0.1:7314)"
|
71
|
+
).should == {
|
72
|
+
:type => :search_result,
|
73
|
+
:open_slots => 2,
|
74
|
+
:slots => 4,
|
75
|
+
:hub => 'TTH:EAAABGHTP4AAACAAAAAAAAAAACIOWCMZ6N7QAAA',
|
76
|
+
:dir => 'Music',
|
77
|
+
:address => '127.0.0.1',
|
78
|
+
:port => 7314,
|
79
|
+
:nick => 'notas'
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'passive searches' do
|
85
|
+
it "correctly parses search results which are files" do
|
86
|
+
helper.parse_message(
|
87
|
+
"$SR notas 02 - Heroes.mp3\0053001347 "+
|
88
|
+
"2/4\005TTH:VZWDTXHBSBSW7UMKCFHNEHXWCSWWYGU4KTIL76A " +
|
89
|
+
"(127.0.0.1:7314)\005user2").should == {
|
90
|
+
:type => :search_result,
|
91
|
+
:open_slots => 2,
|
92
|
+
:slots => 4,
|
93
|
+
:hub => 'TTH:VZWDTXHBSBSW7UMKCFHNEHXWCSWWYGU4KTIL76A',
|
94
|
+
:file => '02 - Heroes.mp3',
|
95
|
+
:address => '127.0.0.1',
|
96
|
+
:port => 7314,
|
97
|
+
:size => 3001347,
|
98
|
+
:nick => 'notas'
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
it "correctly parses search results which are directories" do
|
103
|
+
helper.parse_message(
|
104
|
+
"$SR notas Music 2/4\005" +
|
105
|
+
"TTH:EAAABGHTP4AAACAAAAAAAAAAACIOWCMZ6N7QAAA (127.0.0.1:7314)\005nick"
|
106
|
+
).should == {
|
107
|
+
:type => :search_result,
|
108
|
+
:open_slots => 2,
|
109
|
+
:slots => 4,
|
110
|
+
:hub => 'TTH:EAAABGHTP4AAACAAAAAAAAAAACIOWCMZ6N7QAAA',
|
111
|
+
:dir => 'Music',
|
112
|
+
:address => '127.0.0.1',
|
113
|
+
:port => 7314,
|
114
|
+
:nick => 'notas'
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe 'the ADC draft' do
|
121
|
+
it 'correctly parses the ADCSND command with ZL1' do
|
122
|
+
helper.parse_message(
|
123
|
+
"$ADCSND file TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA 0 1154 ZL1"
|
124
|
+
).should == {
|
125
|
+
:type => :adcsnd,
|
126
|
+
:offset => 0,
|
127
|
+
:file => 'TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA',
|
128
|
+
:size => 1154,
|
129
|
+
:zlib => true,
|
130
|
+
:kind => 'file'
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'correctly parses the ADCSND command without ZL1' do
|
135
|
+
helper.parse_message(
|
136
|
+
"$ADCSND file TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA 4 1154"
|
137
|
+
).should == {
|
138
|
+
:type => :adcsnd,
|
139
|
+
:offset => 4,
|
140
|
+
:file => 'TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA',
|
141
|
+
:size => 1154,
|
142
|
+
:zlib => false,
|
143
|
+
:kind => 'file'
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'correctly parses the ADCGET command with ZL1' do
|
148
|
+
helper.parse_message(
|
149
|
+
"$ADCGET file TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA 0 1154 ZL1"
|
150
|
+
).should == {
|
151
|
+
:type => :adcget,
|
152
|
+
:offset => 0,
|
153
|
+
:file => 'TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA',
|
154
|
+
:size => 1154,
|
155
|
+
:zlib => true,
|
156
|
+
:kind => 'file'
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'correctly parses the ADCGET command without ZL1' do
|
161
|
+
helper.parse_message(
|
162
|
+
"$ADCGET file TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA 4 1154"
|
163
|
+
).should == {
|
164
|
+
:type => :adcget,
|
165
|
+
:offset => 4,
|
166
|
+
:file => 'TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA',
|
167
|
+
:size => 1154,
|
168
|
+
:zlib => false,
|
169
|
+
:kind => 'file'
|
170
|
+
}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
it "parses a chat message correctly" do
|
175
|
+
helper.parse_message('<fargo> hello there!').should == {
|
176
|
+
:type => :chat,
|
177
|
+
:from => 'fargo',
|
178
|
+
:text => 'hello there!'
|
179
|
+
}
|
180
|
+
end
|
181
|
+
|
182
|
+
it "doesn't die on something which it does not match" do
|
183
|
+
helper.parse_message('$NoMatch here').should == {
|
184
|
+
:type => :mystery,
|
185
|
+
:text => '$NoMatch here'
|
186
|
+
}
|
187
|
+
|
188
|
+
helper.parse_message('NoMatch here').should == {
|
189
|
+
:type => :mystery,
|
190
|
+
:text => 'NoMatch here'
|
191
|
+
}
|
192
|
+
|
193
|
+
helper.parse_message('').should == {
|
194
|
+
:type => :mystery,
|
195
|
+
:text => ''
|
196
|
+
}
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fargo::Protocol::DC do
|
4
|
+
let(:conn) { helper_object described_class }
|
5
|
+
let(:client) { Fargo::Client.new }
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
conn.post_init
|
9
|
+
end
|
10
|
+
|
11
|
+
context "receiving data" do
|
12
|
+
it "parses the received data and passes it along to :receive_message" do
|
13
|
+
conn.should_receive(:receive_message).with(:hello,
|
14
|
+
:type => :hello, :nick => 'fargo')
|
15
|
+
|
16
|
+
conn.receive_data '$Hello fargo|'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "handles receiving two chunks of data at once" do
|
20
|
+
conn.should_receive(:receive_message).with(:hello,
|
21
|
+
:type => :hello, :nick => 'fargo').ordered
|
22
|
+
conn.should_receive(:receive_message).with(:hello,
|
23
|
+
:type => :hello, :nick => 'foobar').ordered
|
24
|
+
|
25
|
+
conn.receive_data '$Hello fargo|$Hello foobar|'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "handles receiving data in partial chunks" do
|
29
|
+
conn.should_receive(:receive_message).with(:hello,
|
30
|
+
:type => :hello, :nick => 'fargo').ordered
|
31
|
+
conn.should_receive(:receive_message).with(:hello,
|
32
|
+
:type => :hello, :nick => 'foobar').ordered
|
33
|
+
|
34
|
+
conn.receive_data '$Hello far'
|
35
|
+
conn.receive_data 'go|$Hello'
|
36
|
+
conn.receive_data ' foobar|'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "doesn't call unnecessary methods when the data hasn't been received" do
|
40
|
+
conn.should_receive(:receive_message).with(:hello,
|
41
|
+
:type => :hello, :nick => 'fargo').ordered
|
42
|
+
|
43
|
+
conn.receive_data '$Hello fargo|$Hello fooba'
|
44
|
+
end
|
45
|
+
|
46
|
+
it "passes all binary data to :receive_data_chunk when necessary" do
|
47
|
+
conn.should_receive(:receive_message).with(:hello,
|
48
|
+
:type => :hello, :nick => 'fargo'){ |*args|
|
49
|
+
conn.stub(:parse_data?).and_return false
|
50
|
+
conn.should_receive(:receive_data_chunk).with('asdf')
|
51
|
+
}
|
52
|
+
|
53
|
+
conn.receive_data '$Hello fargo|'
|
54
|
+
conn.receive_data 'asdf'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "sending data" do
|
59
|
+
it "appends a $ to all commands sent with a pipe at the end" do
|
60
|
+
conn.should_receive(:send_data).with('$Foo|')
|
61
|
+
conn.send_message 'Foo'
|
62
|
+
end
|
63
|
+
|
64
|
+
it "allows for arguments to be specified as well" do
|
65
|
+
conn.should_receive(:send_data).with('$Foo bar baz|')
|
66
|
+
conn.send_message 'Foo', 'bar baz'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "alerts the client when it's been disconnected" do
|
71
|
+
conn.client = client
|
72
|
+
client.channel.should_receive(:<<).with(
|
73
|
+
[:dc_disconnected, instance_of(Hash)])
|
74
|
+
|
75
|
+
conn.unbind
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fargo::Protocol::Hub do
|
4
|
+
let(:conn) {
|
5
|
+
helper_object(described_class).tap do |conn|
|
6
|
+
conn.client = Fargo::Client.new
|
7
|
+
conn.post_init
|
8
|
+
end
|
9
|
+
}
|
10
|
+
include Fargo::TTH
|
11
|
+
|
12
|
+
context "searches" do
|
13
|
+
let(:file1) { Fargo.config.download_dir + '/file12' }
|
14
|
+
let(:file2) { Fargo.config.download_dir + '/file23' }
|
15
|
+
let(:file3) { Fargo.config.download_dir + '/file34' }
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
File.open(file1, 'w'){ |f| f << 'garbage' }
|
19
|
+
File.open(file2, 'w'){ |f| f << 'garbage' }
|
20
|
+
File.open(file3, 'w'){ |f| f << 'garbage' }
|
21
|
+
conn.client = Fargo::Client.new
|
22
|
+
conn.client.stub(:nicks).and_return ['foobar']
|
23
|
+
conn.client.share_directory Fargo.config.download_dir
|
24
|
+
conn.post_init
|
25
|
+
end
|
26
|
+
|
27
|
+
it "searches results and sends the results to the hub" do
|
28
|
+
conn.should_receive(:send_message).with('SR',
|
29
|
+
"fargo tmp\\file12\0057 4/4\005TTH:#{file_tth file1} " +
|
30
|
+
"(127.0.0.1:7314)\005foobar")
|
31
|
+
|
32
|
+
query = Fargo::Search.new :query => 'file1'
|
33
|
+
conn.receive_data "$Search Hub:foobar #{query}|"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "sends all hits to the hub" do
|
37
|
+
conn.should_receive(:send_message).with('SR',
|
38
|
+
"fargo tmp\\file23\0057 4/4\005TTH:#{file_tth file2} " +
|
39
|
+
"(127.0.0.1:7314)\005foobar")
|
40
|
+
|
41
|
+
conn.should_receive(:send_message).with('SR',
|
42
|
+
"fargo tmp\\file34\0057 4/4\005TTH:#{file_tth file3} " +
|
43
|
+
"(127.0.0.1:7314)\005foobar")
|
44
|
+
|
45
|
+
query = Fargo::Search.new :query => '3'
|
46
|
+
conn.receive_data "$Search Hub:foobar #{query}|"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "sends active hits via EventMachine" do
|
50
|
+
stub = double 'connection'
|
51
|
+
stub.should_receive(:send_datagram).with('$SR ' +
|
52
|
+
"fargo tmp\\file12\0057 4/4\005TTH:#{file_tth file1} " +
|
53
|
+
"(127.0.0.1:7314)|", '127.0.0.1', 7000).ordered
|
54
|
+
stub.should_receive(:close_connection_after_writing).ordered
|
55
|
+
|
56
|
+
EventMachine.stub(:open_datagram_socket).with('0.0.0.0', 0).
|
57
|
+
and_return stub
|
58
|
+
|
59
|
+
query = Fargo::Search.new :query => 'file1'
|
60
|
+
conn.receive_data "$Search 127.0.0.1:7000 #{query}|"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "$RevConnectToMe" do
|
65
|
+
it "responds with $ConnectToMe when the client is active" do
|
66
|
+
conn.client.config.passive = false
|
67
|
+
conn.client.config.address = '1.2.3.4'
|
68
|
+
conn.client.config.active_port = 728
|
69
|
+
conn.should_receive(:send_data).with('$ConnectToMe foobar 1.2.3.4:728|')
|
70
|
+
|
71
|
+
conn.receive_data '$RevConnectToMe foobar fargo|'
|
72
|
+
end
|
73
|
+
|
74
|
+
it "responds with $RevConnectToMe when the client is passive" do
|
75
|
+
conn.client.config.passive = true
|
76
|
+
conn.should_receive(:send_data).with('$RevConnectToMe fargo foobar|')
|
77
|
+
|
78
|
+
conn.receive_data '$RevConnectToMe foobar fargo|'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it "connects to a client when receiving $ConnectToMe" do
|
83
|
+
EventMachine.should_receive(:connect).with('1.2.3.4', 500,
|
84
|
+
Fargo::Protocol::Peer)
|
85
|
+
|
86
|
+
conn.receive_data '$ConnectToMe fargo 1.2.3.4:500|'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "proxies specifically unhandled messages to the client" do
|
90
|
+
conn.client.channel.should_receive(:<<).with [:nick_list, instance_of(Hash)]
|
91
|
+
|
92
|
+
conn.receive_data '$NickList a$$b|'
|
93
|
+
end
|
94
|
+
|
95
|
+
it "says that :hub_disconnected when the hub disconnects" do
|
96
|
+
conn.client.channel.should_receive(:<<).with(
|
97
|
+
[:hub_disconnected, instance_of(Hash)])
|
98
|
+
|
99
|
+
conn.unbind
|
100
|
+
end
|
101
|
+
|
102
|
+
context "the hub handshake" do
|
103
|
+
before :each do
|
104
|
+
Fargo.configure do |config|
|
105
|
+
config.nick = 'fargo'
|
106
|
+
config.speed = 'DSL'
|
107
|
+
config.override_share_size = nil
|
108
|
+
config.email = 'asdf'
|
109
|
+
config.passive = false
|
110
|
+
config.upload_slots = 5
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it "replies with a valid key when the lock is sent" do
|
115
|
+
conn.should_receive(:send_data).with "$Key 4\220/%DCN000%/|"
|
116
|
+
|
117
|
+
conn.receive_data "$Lock FOO Pk=BAR|"
|
118
|
+
end
|
119
|
+
|
120
|
+
it "tries to validate the client's nick when the $HubName is received" do
|
121
|
+
conn.should_receive(:send_data).with "$ValidateNick fargo|"
|
122
|
+
|
123
|
+
conn.receive_data '$HubName foobar|'
|
124
|
+
end
|
125
|
+
|
126
|
+
it "sends $Version, $MyInfo, and $GetNickList upon confirmation of nick" do
|
127
|
+
conn.should_receive(:send_data).with('$Version 1,0091|').ordered
|
128
|
+
conn.should_receive(:send_data).with(
|
129
|
+
"$MyINFO $ALL fargo <fargo V:#{Fargo::VERSION},M:A,H:1/0/0,S:5," +
|
130
|
+
"Dt:1.2.6/W>$ $DSL\001$asdf$0$|").ordered
|
131
|
+
conn.should_receive(:send_data).with('$GetNickList|').ordered
|
132
|
+
|
133
|
+
conn.receive_data '$Hello fargo|'
|
134
|
+
end
|
135
|
+
|
136
|
+
it "sends the configured password when requested" do
|
137
|
+
conn.client.config.password = 'foobar'
|
138
|
+
conn.should_receive(:send_data).with('$MyPass foobar|')
|
139
|
+
|
140
|
+
conn.receive_data '$GetPass|'
|
141
|
+
end
|
142
|
+
|
143
|
+
it "disconnects when the password was bad" do
|
144
|
+
conn.should_receive(:close_connection_after_writing)
|
145
|
+
|
146
|
+
conn.receive_data '$BadPass|'
|
147
|
+
end
|
148
|
+
|
149
|
+
it "disconnects when the hub is full" do
|
150
|
+
conn.should_receive(:close_connection_after_writing)
|
151
|
+
|
152
|
+
conn.receive_data '$HubIsFull|'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|