fargo 0.1.1 → 0.2.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/lib/fargo/client.rb CHANGED
@@ -1,16 +1,15 @@
1
- require 'active_support/callbacks'
2
- require 'active_support/configurable'
1
+ require 'socket'
3
2
  require 'active_support/core_ext/object/try'
3
+ require 'active_support/callbacks'
4
4
 
5
5
  module Fargo
6
6
  class Client
7
7
 
8
- include ActiveSupport::Callbacks
9
8
  include ActiveSupport::Configurable
9
+ include ActiveSupport::Callbacks
10
10
 
11
- define_callbacks :setup
11
+ define_callbacks :initialization
12
12
 
13
- include Fargo::Publisher
14
13
  include Fargo::Supports::Chat
15
14
  include Fargo::Supports::Uploads
16
15
  include Fargo::Supports::NickList
@@ -20,141 +19,71 @@ module Fargo
20
19
  include Fargo::Supports::Timeout
21
20
  include Fargo::Supports::FileList
22
21
 
23
- # Options
24
- # :hub_port
25
- # :hub_address
26
- # :search_port
27
- # :active_port
28
- # :nick
29
- # :password
30
- # :email
31
- # :speed
32
- # :passive
33
- # :download_slots
34
- # :download_dir
35
- # :slots
36
22
  configure do |config|
37
- config.download_dir = '/tmp/fargo/downloads'
38
- config.version = '0.75'
39
- # default the address to the address of this machine
40
- config.address = IPSocket.getaddress(Socket.gethostname)
41
- config.passive = true
42
- config.nick = 'fargo'
23
+ config.download_dir = '/tmp/fargo/downloads'
24
+ config.address = IPSocket.getaddress(Socket.gethostname)
25
+ config.passive = false
26
+ config.nick = 'fargo'
27
+ config.hub_address = '127.0.0.1'
28
+ config.hub_port = 7314
29
+ config.active_port = 7315
30
+ config.search_port = 7316
31
+ config.download_slots = 4
32
+ config.upload_slots = 4
33
+ config.password = ''
34
+ config.speed = 'DSL'
35
+ config.email = nil
43
36
  end
44
37
 
45
- attr_reader :hub, :searcher, :active_server
38
+ attr_reader :hub, :channel
46
39
 
47
40
  def initialize
48
- @connection_timeout_threads = {}
49
- end
50
-
51
- # Don't do this in initialization so we have time to set all the options
52
- def setup
53
- @hub = Fargo::Connection::Hub.new self
54
- @hub.config.port = config.hub_port if config.hub_port
55
- @hub.config.address = config.hub_address if config.hub_address
56
-
57
- unless config.passive
58
- # TODO: get this working again
59
- # Always create a search connection for this.
60
- # searcher_options = new_options.merge :port => search_port,
61
- # :connection => Fargo::Connection::Search
62
- # @searcher = Fargo::Server.new searcher_options
63
- #
64
- # # For now, being active means that you can only download things. Always make a
65
- # # connection which downloads things.
66
- # active_options = new_options.merge :port => active_port,
67
- # :connection => Fargo::Connection::Download
68
- # @active_server = Fargo::Server.new active_options
69
- end
41
+ run_callbacks :initialization do
42
+ @channel = EventMachine::Channel.new
43
+ @connection_timeouts = {}
70
44
 
71
- run_callbacks :setup
72
- end
73
-
74
- def get_info nick
75
- hub.write "$GetINFO #{nick} #{config.nick}"
76
- end
77
-
78
- def get_ip *nicks
79
- hub.write "$UserIP #{nicks.flatten.join '$$'}"
80
- end
81
-
82
- def connect_with nick
83
- @connection_timeout_threads[nick] = Thread.start do
84
- sleep 10
85
- connection_timeout! nick
86
- end
87
-
88
- if config.passive
89
- hub.write "$RevConnectToMe #{self.config.nick} #{nick}"
90
- else
91
- hub.write "$ConnectToMe #{nick} #{config.address}:#{config.active_port}"
45
+ @channel.subscribe do |type, hash|
46
+ Fargo.logger.debug "Channel received: #{type} - #{hash.inspect}"
47
+ end
92
48
  end
93
49
  end
94
50
 
95
- def connected_with! nick
96
- return unless @connection_timeout_threads.has_key?(nick)
97
- @connection_timeout_threads.delete(nick).exit
98
- end
99
-
100
51
  def connect
101
- setup if hub.nil?
102
-
103
- # connect all our associated servers
104
- hub.connect
52
+ EventMachine.error_handler{ |e|
53
+ Fargo.logger.debug "Error raised during event loop: #{e.message}"
54
+ Fargo.logger.debug e.backtrace.join("\n")
55
+ }
56
+
57
+ EventMachine.connect config.hub_address, config.hub_port,
58
+ Fargo::Protocol::Hub do |conn|
59
+ @hub = conn
60
+ @hub.client = self
61
+ end
105
62
 
106
63
  unless config.passive
107
- searcher.connect
108
- active_server.connect
64
+ EventMachine.start_server '0.0.0.0', config.active_port,
65
+ Fargo::Protocol::Download do |conn|
66
+ conn.client = self
67
+ end
68
+
69
+ EventMachine.open_datagram_socket '0.0.0.0', config.search_port,
70
+ Fargo::Protocol::DC do |conn|
71
+ conn.client = self
72
+ end
109
73
  end
110
-
111
- true
112
74
  end
113
75
 
114
76
  def connected?
115
- hub.try :connected?
77
+ EventMachine.reactor_running?
116
78
  end
117
79
 
118
80
  def disconnect
119
- return if hub.nil?
120
-
121
- Fargo.logger.info "Disconnecting from hub."
122
- hub.disconnect
123
- unless config.passive
124
- searcher.disconnect
125
- active_server.disconnect
126
- end
127
- end
128
-
129
- def search_hub query
130
- raise ConnectionError.new('Not connected Yet!') if hub.nil?
131
-
132
- if config.passive
133
- location = "Hub:#{config.nick}"
134
- else
135
- location = "#{config.address}:#{config.search_port}"
136
- end
137
-
138
- hub.write "$Search #{location} #{query.to_s}"
139
- end
140
-
141
- # see hub/parser#@@search for what's passed in
142
- # searches this client's files based on those options and returns an array
143
- # of SearchResult(s)
144
- def search_files options
145
- # TODO: implement me
146
- []
81
+ Fargo.logger.info 'Disconnecting from hub.'
82
+ EventMachine.stop_event_loop
147
83
  end
148
84
 
149
85
  def description
150
- "<++ V:#{config.version},M:#{config.passive ? 'P' : 'A'},H:1/0/0,S:#{open_slots},Dt:1.2.0/W>"
151
- end
152
-
153
- private
154
-
155
- def connection_timeout! nick
156
- @connection_timeout_threads.delete(nick)
157
- publish :connection_timeout, :nick => nick
86
+ "<fargo V:#{Fargo::VERSION},M:#{config.passive ? 'P' : 'A'},H:1/0/0,S:#{open_slots},Dt:1.2.6/W>"
158
87
  end
159
88
 
160
89
  end
@@ -0,0 +1,52 @@
1
+ module Fargo
2
+ module Protocol
3
+ module DC
4
+
5
+ include Fargo::Parser
6
+ attr_accessor :client
7
+
8
+ def post_init
9
+ @received_data = ''
10
+ end
11
+
12
+ def receive_message type, message
13
+ client.channel << [type, message] if client
14
+ end
15
+
16
+ def send_message method, args = nil
17
+ if args
18
+ data = "$#{method} #{args}|"
19
+ else
20
+ data = "$#{method}|"
21
+ end
22
+
23
+ Fargo.logger.debug "#{self} Sending: #{data.inspect}"
24
+ send_data data
25
+ end
26
+
27
+ def receive_data data
28
+ @received_data << data
29
+
30
+ while message = @received_data.slice!(/[^\|]+\|/)
31
+ message.chomp! '|'
32
+ Fargo.logger.debug "#{self}: Received: #{message.inspect}"
33
+ hash = parse_message message
34
+ receive_message hash[:type], hash
35
+ end
36
+ end
37
+
38
+ def publish_args
39
+ {}
40
+ end
41
+
42
+ def unbind
43
+ if client
44
+ connection_type = self.class.name.split('::').last.downcase
45
+ args = [:"#{connection_type}_disconnected", publish_args]
46
+ client.channel << args
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -1,44 +1,36 @@
1
1
  require 'zlib'
2
2
 
3
3
  module Fargo
4
- module Connection
5
- class Download < Base
4
+ module Protocol
5
+ class Download < EventMachine::Connection
6
6
 
7
7
  include Fargo::Utils
8
- include Fargo::Parser
8
+ include Fargo::Protocol::DC
9
9
 
10
- set_callback :listen, :before, :pre_listen
11
- set_callback :listen, :after do |connection|
12
- send_lock if connection.config.first
13
- end
10
+ attr_accessor :download, :client
14
11
 
15
- attr_accessor :download
12
+ def post_init
13
+ super
16
14
 
17
- def pre_listen
18
- Fargo.logger.debug "Initiating connection on: #{config.address}:#{config.port}"
15
+ set_comm_inactivity_timeout 20
19
16
 
20
- config.quit_on_disconnect = false
21
- @lock, @pk = generate_lock
17
+ @lock, @pk = generate_lock
22
18
  @handshake_step = 0
23
-
24
- @buffer_size = (2 << 12).freeze
25
19
  end
26
20
 
27
21
  def send_lock
28
- write "$MyNick #{@client.config.nick}|$Lock #{@lock} Pk=#{@pk}"
22
+ @lock_sent = true
23
+ send_message 'MyNick', @client.config.nick
24
+ send_message 'Lock', "#{@lock} Pk=#{@pk}"
29
25
  end
30
26
 
31
- def read_data
32
- # only download if we're at the correct time
27
+ def receive_data data
28
+ # only download if we're at the correct handshake step
33
29
  return super if @handshake_step != 6
34
30
 
35
- @exit_time = 20 # reset our timeout time
36
-
37
- data = @socket.readpartial @buffer_size
38
-
39
31
  if @zlib
40
- @zs = Zlib::Inflate.new if @zs.nil?
41
- data = @zs.inflate data
32
+ @inflator = Zlib::Inflate.new if @inflator.nil?
33
+ data = @inflator.inflate data
42
34
  end
43
35
 
44
36
  @file << data
@@ -51,33 +43,29 @@ module Fargo
51
43
  percent = @recvd.to_f / @length
52
44
  if percent - @last_published > 0.05
53
45
  @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
46
+ @client.channel << [:download_progress, {:percent => percent,
47
+ :file => download_path,
48
+ :nick => @other_nick,
49
+ :download => @download,
50
+ :size => @recvd,
51
+ :compressed => @zlib}]
60
52
 
61
53
  @last_published = percent
62
54
  end
63
55
 
64
56
  download_finished! if @recvd == @length
65
57
  end
66
- rescue IOError => e
67
- error "#{self}: IOError, disconnecting #{e}"
68
58
  end
69
59
 
70
- def receive data
71
- message = parse_message data
72
-
73
- case message[:type]
60
+ def receive_message type, message
61
+ case type
74
62
  when :mynick
75
63
  if @handshake_step == 0
76
64
  @handshake_step = 1
77
65
  @other_nick = message[:nick]
78
66
 
79
- @client.connected_with! @other_nick
80
- @client.lock_connection_with! @other_nick, self
67
+ client.channel << [:download_opened,
68
+ publish_args.merge(:connection => self)]
81
69
  @download = @client.lock_next_download! @other_nick, self
82
70
 
83
71
  if @download.try(:file).nil?
@@ -91,12 +79,12 @@ module Fargo
91
79
  if @handshake_step == 1
92
80
  @remote_lock = message[:lock]
93
81
  @handshake_step = 2
94
- send_lock unless config.first
95
- out = ''
96
- out << '$Supports TTHF ADCGet ZLIG|'
97
- out << "$Direction Download #{@my_num = rand(10000)}|"
98
- out << "$Key #{generate_key @remote_lock}|"
99
- write out
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)
100
88
  else
101
89
  error 'Premature disconnect when lock received'
102
90
  end
@@ -136,18 +124,18 @@ module Fargo
136
124
  @zlib = message[:zlib]
137
125
  @length = message[:size]
138
126
 
139
- write "$Send" unless @client_extensions.include? 'ADCGet'
127
+ send_message 'Send' unless @client_extensions.include? 'ADCGet'
140
128
 
141
- publish :download_started, :file => download_path,
142
- :download => @download,
143
- :nick => @other_nick
129
+ @client.channel << [:download_started, {:file => download_path,
130
+ :download => @download,
131
+ :nick => @other_nick}]
144
132
  else
145
133
  error "Premature disconnect when #{message[:type]} received"
146
134
  end
147
135
 
148
136
  when :noslots
149
137
  if @download
150
- Fargo.logger.debug "#{self}: No Slots for #{self[:download]}"
138
+ Fargo.logger.debug "#{self}: No Slots for #{@download}"
151
139
 
152
140
  download_failed! 'No Slots'
153
141
  end
@@ -158,7 +146,7 @@ module Fargo
158
146
 
159
147
  # This wasn't handled by us, proxy it on up to the client
160
148
  else
161
- @client.publish message[:type], message
149
+ super
162
150
 
163
151
  end
164
152
  end
@@ -167,8 +155,6 @@ module Fargo
167
155
  @file = File.open download_path, 'wb'
168
156
 
169
157
  @file.seek @download.offset
170
- @file.sync = true
171
- @socket.sync = true
172
158
  @handshake_step = 5
173
159
  @last_published = 0
174
160
 
@@ -194,24 +180,11 @@ module Fargo
194
180
  Fargo.logger.debug "Enabling zlib compression on: #{@download.file}"
195
181
  end
196
182
 
197
- write "$ADCGET file #{download_query} #{@download.offset} #{@download.size} #{zlig}"
183
+ send_message 'ADCGET', "file #{download_query} #{@download.offset} #{@download.size} #{zlig}"
198
184
  else
199
- write "$Get #{@download.file}$#{@download.offset + 1}"
185
+ send_message 'Get', "#{@download.file}$#{@download.offset + 1}"
200
186
  end
201
187
 
202
- # This is the thread for the timeout of a connection. The @exit_time
203
- # variable is reset to 20 after every bit of information is received.
204
- @exit_time = 20
205
- @exit_thread = Thread.start {
206
- while @exit_time > 0
207
- sleep 1
208
- @exit_time -= 1
209
- Fargo.logger.debug "#{self} time out in #{@exit_time} seconds"
210
- end
211
-
212
- download_failed! 'Download timeout!'
213
- }
214
-
215
188
  Fargo.logger.debug "#{self}: Beginning download of #{@download}"
216
189
  end
217
190
 
@@ -224,12 +197,10 @@ module Fargo
224
197
 
225
198
  reset_download
226
199
 
227
- publish :download_failed, opts.merge(:nick => @other_nick,
200
+ @client.channel << [:download_failed, opts.merge(:nick => @other_nick,
228
201
  :download => download,
229
202
  :file => path,
230
- :last_error => msg)
231
-
232
- @exit_thread = nil
203
+ :last_error => msg)]
233
204
  end
234
205
 
235
206
  def download_finished!
@@ -241,16 +212,21 @@ module Fargo
241
212
 
242
213
  reset_download
243
214
 
244
- publish :download_finished, :file => path, :download => download,
245
- :nick => @other_nick
246
- disconnect if download.file_list?
215
+ @client.channel << [:download_finished,
216
+ {:file => path, :download => download, :nick => @other_nick}]
217
+
218
+ close_connection_after_writing if download.file_list?
247
219
  end
248
220
 
249
- def disconnect
250
- Fargo.logger.debug "#{self} Disconnecting from: #{@other_nick}"
221
+ def publish_args
222
+ {:nick => @other_nick}
223
+ end
251
224
 
225
+ def unbind
252
226
  super
253
227
 
228
+ Fargo.logger.debug "#{self} Disconnected from: #{@other_nick}"
229
+
254
230
  if @download
255
231
  download_failed! @last_error, :recvd => @recvd, :length => @length
256
232
  end
@@ -259,25 +235,16 @@ module Fargo
259
235
  end
260
236
 
261
237
  private
238
+
262
239
  def reset_download
263
240
  @file.close unless @file.nil? || @file.closed?
241
+
264
242
  if @file_path && File.exists?(@file_path) && File.size(@file_path) == 0
265
243
  File.delete(@file_path)
266
244
  end
267
245
 
268
- if @socket
269
- @socket.sync = false
270
- @socket.flush
271
- end
272
-
273
- # If this was called from exit thread, don't kill it
274
- if @exit_thread != Thread.current
275
- @exit_thread.exit if @exit_thread && @exit_thread.alive?
276
- @exit_thread = nil
277
- end
278
-
279
246
  # clear out these variables
280
- @zs = @file_path = @zlib = @download = @length = @recvd = nil
247
+ @inflator = @file_path = @zlib = @download = @length = @recvd = nil
281
248
 
282
249
  # Go back to the get step
283
250
  @handshake_step = 5
@@ -305,7 +272,8 @@ module Fargo
305
272
 
306
273
  def error message
307
274
  Fargo.logger.warn @last_error = message
308
- disconnect
275
+
276
+ close_connection
309
277
  end
310
278
 
311
279
  end
@@ -0,0 +1,81 @@
1
+ module Fargo
2
+ module Protocol
3
+ class Hub < EventMachine::Connection
4
+
5
+ include Fargo::Protocol::DC
6
+ include Fargo::Utils
7
+
8
+ attr_reader :hubname
9
+
10
+ # See <http://www.teamfair.info/DC-Protocol.htm> for specifics on
11
+ # the DC protocol
12
+ def receive_message type, message
13
+ case type
14
+ when :lock
15
+ @validated = false
16
+ send_message 'Key', generate_key(message[:lock])
17
+ when :hubname
18
+ @hubname = message[:name]
19
+ send_message 'ValidateNick', @client.config.nick unless @validated
20
+ when :getpass
21
+ send_message 'MyPass', @client.config.password
22
+ when :badpass, :hubfull
23
+ Fargo.logger.warn "Disconnecting because of: #{message.inspect}"
24
+ close_connection_after_writing
25
+ when :hello
26
+ if message[:who] == @client.config.nick
27
+ Fargo.logger.info "Connected to DC Hub #{@hubname}"
28
+ @validated = true
29
+
30
+ send_message 'Version', '1,0091'
31
+ send_message 'MyINFO', "$ALL #{@client.config.nick} " +
32
+ "#{@client.description}$ $#{@client.config.speed}" +
33
+ "#{@status || 1.chr}$#{@client.config.email}" +
34
+ "$#{@client.share_size}$"
35
+ send_message 'GetNickList'
36
+ end
37
+
38
+ when :connect_to_me
39
+ if !@client.nicks.include?(message[:nick])
40
+ Fargo.logger.info "Invalid connect_to_me request from: #{message[:nick]}"
41
+ return
42
+ end
43
+
44
+ EventMachine.connect message[:address], message[:port],
45
+ Fargo::Protocol::Download do |conn|
46
+ conn.client = @client
47
+ conn.send_lock # We connect first, we send lock first
48
+ end
49
+
50
+ when :search
51
+ # Let the client handle the results
52
+ @results = @client.search_files message
53
+
54
+ # Send all the results to the peer. Take care of active/passive
55
+ # connections
56
+ @results.each { |r|
57
+ if message[:address]
58
+ r.active_send @client.config.nick, message[:ip], message[:port]
59
+ else
60
+ send_message 'SR', "#{@client.config.nick} #{r}"
61
+ end
62
+ }
63
+
64
+ when :revconnect
65
+ if @client.config.passive
66
+ send_message 'RevConnectToMe',
67
+ "#{@client.config.nick} #{message[:who]}"
68
+ else
69
+ send_message 'ConnectToMe', "#{@client.config.nick} #{@client.config.address}:#{@client.config.extport}"
70
+ end
71
+
72
+ # proxy this message on up the stack if we don't handle it
73
+ else
74
+ super
75
+
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
data/lib/fargo/search.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Fargo
2
2
  class Search
3
-
3
+
4
4
  ANY = 1
5
5
  AUDIO = 2
6
6
  COMPRESSED = 3
@@ -8,38 +8,43 @@ module Fargo
8
8
  EXECUTABLE = 5
9
9
  VIDEO = 7
10
10
  FOLDER = 8
11
-
11
+
12
12
  attr_accessor :size_restricted, :is_minimum_size, :size, :filetype, :pattern
13
-
13
+
14
14
  def initialize opts = {}
15
- self.size_restricted = opts[:size_restricted]
16
- self.is_minimum_size = opts[:is_minimum_size]
17
- self.size = opts[:size]
18
- self.filetype = opts[:filetype] || ANY
15
+ @size_restricted = opts[:size_restricted]
16
+ @is_minimum_size = opts[:is_minimum_size]
17
+ @size = opts[:size]
18
+ @filetype = opts[:filetype] || ANY
19
+
19
20
  if opts[:pattern]
20
- self.pattern = opts[:pattern]
21
+ @pattern = opts[:pattern]
21
22
  elsif opts[:query]
22
23
  self.query = opts[:query]
23
24
  end
24
25
  end
25
-
26
+
26
27
  def query= query
27
28
  @pattern = query.split(' ').join('$')
28
29
  end
29
-
30
+
30
31
  def queries
31
- pattern.split("$")
32
+ pattern.split('$')
32
33
  end
33
34
 
34
35
  def query
35
36
  pattern.gsub('$', ' ')
36
37
  end
37
-
38
+
38
39
  def matches_result? map
39
40
  file = map[:file].downcase
40
- matches_query = queries.inject(true) { |last, word| last && file.index(word.downcase) }
41
+
42
+ matches_query = queries.inject(true) do |last, word|
43
+ last && file.index(word.downcase)
44
+ end
45
+
41
46
  if size_restricted == 'T'
42
- if is_minimum_size
47
+ if is_minimum_size
43
48
  matches_query && map[:size] > size
44
49
  else
45
50
  matches_query && map[:size] < size
@@ -51,11 +56,11 @@ module Fargo
51
56
 
52
57
  def to_s
53
58
  if size_restricted
54
- "#{size_restricted ? 'T' : 'F' }?#{!size_restricted || is_minimum_size ? 'T' : 'F'}?#{size || 0}?#{filetype}?#{pattern}"
59
+ "#{size_restricted ? 'T' : 'F' }?#{!size_restricted || is_minimum_size ? 'T' : 'F'}?#{size || 0}?#{filetype}?#{pattern}"
55
60
  else
56
61
  "F?T?#{size || 0}?#{filetype}?#{pattern}"
57
62
  end
58
63
  end
59
-
64
+
60
65
  end
61
66
  end