fargo 0.1.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 +161 -0
- data/lib/fargo/connection/base.rb +133 -0
- data/lib/fargo/connection/download.rb +302 -0
- data/lib/fargo/connection/hub.rb +120 -0
- data/lib/fargo/connection/search.rb +19 -0
- data/lib/fargo/connection/upload.rb +85 -0
- data/lib/fargo/parser.rb +121 -0
- data/lib/fargo/publisher.rb +26 -0
- data/lib/fargo/search.rb +61 -0
- data/lib/fargo/search_result.rb +31 -0
- data/lib/fargo/server.rb +52 -0
- data/lib/fargo/supports/chat.rb +35 -0
- data/lib/fargo/supports/downloads.rb +261 -0
- data/lib/fargo/supports/file_list.rb +84 -0
- data/lib/fargo/supports/nick_list.rb +71 -0
- data/lib/fargo/supports/persistence.rb +52 -0
- data/lib/fargo/supports/searches.rb +64 -0
- data/lib/fargo/supports/timeout.rb +19 -0
- data/lib/fargo/supports/uploads.rb +16 -0
- data/lib/fargo/utils.rb +35 -0
- data/lib/fargo/version.rb +3 -0
- data/lib/fargo.rb +48 -0
- metadata +127 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'bzip2'
|
2
|
+
require 'libxml'
|
3
|
+
|
4
|
+
module Fargo
|
5
|
+
module Supports
|
6
|
+
module FileList
|
7
|
+
class Listing < Struct.new(:tth, :size, :name); end
|
8
|
+
|
9
|
+
# Lazily load the file list for the nick. Subscribe to the client for the
|
10
|
+
# event :file_list to get notified.
|
11
|
+
def file_list nick
|
12
|
+
@file_list ||= {}
|
13
|
+
return parse_file_list(@file_list[nick]) if @file_list.has_key?(nick)
|
14
|
+
|
15
|
+
file_gotten = lambda{ |type, map|
|
16
|
+
case type
|
17
|
+
when :download_finished, :download_failed, :connection_timeout
|
18
|
+
if map[:nick] == nick
|
19
|
+
@file_list[nick] = map[:file]
|
20
|
+
unsubscribe &file_gotten
|
21
|
+
publish :file_list, :nick => nick, :list => @file_list[nick]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
}
|
25
|
+
|
26
|
+
subscribe &file_gotten
|
27
|
+
|
28
|
+
download nick, 'files.xml.bz2'
|
29
|
+
end
|
30
|
+
|
31
|
+
# Wait for the results to arrive, timed out after some time
|
32
|
+
def file_list! nick, timeout = 10
|
33
|
+
@file_list ||= {}
|
34
|
+
return parse_file_list(@file_list[nick]) if @file_list.has_key?(nick)
|
35
|
+
|
36
|
+
list = nil
|
37
|
+
list_gotten = lambda{ |type, map|
|
38
|
+
if type == :file_list && map[:nick] == nick
|
39
|
+
list = map[:list]
|
40
|
+
true
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
timeout_response(timeout, list_gotten){ file_list nick }
|
47
|
+
|
48
|
+
parse_file_list list
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def parse_file_list file
|
54
|
+
if file && File.exists?(file)
|
55
|
+
xml = Bzip2::Reader.open(file).read
|
56
|
+
doc = LibXML::XML::Document.string xml
|
57
|
+
|
58
|
+
construct_file_list doc.root
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def construct_file_list node
|
65
|
+
list = {}
|
66
|
+
|
67
|
+
node.each_element do |element|
|
68
|
+
element_name = element['Name']
|
69
|
+
if element.name =~ /directory/i
|
70
|
+
list[element_name] = construct_file_list element
|
71
|
+
else
|
72
|
+
list[element_name] = Listing.new(
|
73
|
+
element['TTH'],
|
74
|
+
element['Size'],
|
75
|
+
element_name
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
list
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Fargo
|
2
|
+
module Supports
|
3
|
+
module NickList
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
set_callback :setup, :after, :subscribe_to_nicks
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :nicks
|
11
|
+
|
12
|
+
def info nick
|
13
|
+
return nil unless @nick_info
|
14
|
+
if @nick_info.has_key?(nick) || !connected? || !@nicks.include?(nick)
|
15
|
+
return @nick_info[nick]
|
16
|
+
end
|
17
|
+
|
18
|
+
# If we're connected and we don't already have this user's info, ask the
|
19
|
+
# server. We'll wait for 5 second to respond, otherwise we'll just
|
20
|
+
# return nil and be done with it
|
21
|
+
info_gotten = lambda{ |type, map|
|
22
|
+
map[:type] == :myinfo && map[:nick].to_s == nick.to_s
|
23
|
+
}
|
24
|
+
timeout_response(5, info_gotten){ get_info nick }
|
25
|
+
|
26
|
+
@nick_info[nick]
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_slot? nick
|
30
|
+
# This query must be up to date so remove any cached information we have
|
31
|
+
# about the nick so we can get a fresh copy
|
32
|
+
@nick_info.try :delete, nick
|
33
|
+
info = info nick
|
34
|
+
|
35
|
+
return false if info.nil?
|
36
|
+
return true if info[:interest].nil?
|
37
|
+
|
38
|
+
match = info[:interest].match /.*?<.*S:(\d+).*>/
|
39
|
+
return true if match.nil?
|
40
|
+
|
41
|
+
Fargo.logger.debug "#{self} User: #{nick} has #{match[1]} open slots"
|
42
|
+
match[1].to_i > 0
|
43
|
+
end
|
44
|
+
|
45
|
+
def subscribe_to_nicks
|
46
|
+
@nicks = []
|
47
|
+
@nick_info = Hash.new{ |h, k| h[k] = {} }
|
48
|
+
|
49
|
+
subscribe do |type, map|
|
50
|
+
case type
|
51
|
+
when :hello
|
52
|
+
@nicks << map[:who] unless @nicks.include? map[:who]
|
53
|
+
when :myinfo
|
54
|
+
@nick_info[map[:nick]] = map
|
55
|
+
when :nick_list
|
56
|
+
@nicks = map[:nicks]
|
57
|
+
when :quit
|
58
|
+
@nicks.delete map[:who]
|
59
|
+
@nick_info.delete map[:who]
|
60
|
+
when :hub_disconnected
|
61
|
+
@nicks.clear
|
62
|
+
@nick_info.clear
|
63
|
+
when :userip
|
64
|
+
map[:users].each_pair{ |nick, ip| @nick_info[nick][:ip] = ip }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Fargo
|
2
|
+
module Supports
|
3
|
+
module Persistence
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
set_callback :setup, :after, :setup_connection_cache
|
8
|
+
end
|
9
|
+
|
10
|
+
def lock_connection_with! nick, connection
|
11
|
+
@connection_cache[nick] = connection
|
12
|
+
end
|
13
|
+
|
14
|
+
def connection_for nick
|
15
|
+
c = @connection_cache.try :[], nick
|
16
|
+
return c if c.nil? || c.connected?
|
17
|
+
|
18
|
+
# If it's present and not connected, remove it from the cache
|
19
|
+
@connection_cache.try :delete, nick
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def connected_with? nick
|
24
|
+
c = @connection_cache.try :[], nick
|
25
|
+
c.connected? unless c.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def disconnect_from nick
|
29
|
+
c = @connection_cache.try :delete, nick
|
30
|
+
c.disconnect unless c.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def nicks_connected_with
|
34
|
+
return [] if @connection_cache.nil?
|
35
|
+
|
36
|
+
nicks = @connection_cache.keys
|
37
|
+
nicks.reject{ |n| !connected_with? n }
|
38
|
+
end
|
39
|
+
|
40
|
+
def setup_connection_cache
|
41
|
+
@connection_cache = {}
|
42
|
+
|
43
|
+
subscribe { |type, hash|
|
44
|
+
if type == :hub_disconnected
|
45
|
+
nicks_connected_with.each{ |n| disconnect_from n }
|
46
|
+
end
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Fargo
|
2
|
+
module Supports
|
3
|
+
module Searches
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
set_callback :setup, :after, :subscribe_to_searches
|
8
|
+
end
|
9
|
+
|
10
|
+
def search search
|
11
|
+
raise ConnectionException.new 'Not connected yet!' unless connected?
|
12
|
+
|
13
|
+
search = normalize search
|
14
|
+
@searches[search.to_s] = []
|
15
|
+
@search_objects[search.to_s] = search
|
16
|
+
search_hub search
|
17
|
+
end
|
18
|
+
|
19
|
+
def searches
|
20
|
+
@searches.keys.map { |k| @search_objects[k] } if @searches
|
21
|
+
end
|
22
|
+
|
23
|
+
def search_results search
|
24
|
+
search = normalize search
|
25
|
+
@searches[search.to_s] if @searches
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove_search search
|
29
|
+
search = normalize search
|
30
|
+
@searches.delete search.to_s if @searches
|
31
|
+
@search_objects.delete search.to_s if @search_objects
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def normalize search
|
37
|
+
unless search.is_a? Fargo::Search
|
38
|
+
search = Fargo::Search.new :query => search
|
39
|
+
end
|
40
|
+
|
41
|
+
search
|
42
|
+
end
|
43
|
+
|
44
|
+
def subscribe_to_searches
|
45
|
+
@searches = {}
|
46
|
+
@search_objects = {}
|
47
|
+
|
48
|
+
subscribe do |type, map|
|
49
|
+
if type == :search_result
|
50
|
+
@searches.keys.each do |search|
|
51
|
+
if @search_objects[search].matches_result?(map)
|
52
|
+
@searches[search] << map
|
53
|
+
end
|
54
|
+
end
|
55
|
+
elsif type == :hub_disconnected
|
56
|
+
@searches.clear
|
57
|
+
@search_objects.clear
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Fargo
|
2
|
+
module Supports
|
3
|
+
module Timeout
|
4
|
+
|
5
|
+
def timeout_response timeout, succeed_subscription
|
6
|
+
thread = Thread.current
|
7
|
+
subscribed_block = lambda do |*args|
|
8
|
+
thread.wakeup if succeed_subscription.call(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
subscribe &subscribed_block
|
12
|
+
yield
|
13
|
+
sleep timeout
|
14
|
+
unsubscribe &subscribed_block
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/fargo/utils.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Fargo
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
# Lord knows why they're doing this...
|
5
|
+
def generate_key lock
|
6
|
+
lock_bytes = lock.bytes.to_a
|
7
|
+
bytes = []
|
8
|
+
bytes << (lock_bytes[0] ^ lock_bytes[-1] ^ lock_bytes[-2] ^ 5)
|
9
|
+
(1..lock.length-1).each{ |i|
|
10
|
+
bytes << (lock_bytes[i] ^ lock_bytes[i - 1])
|
11
|
+
}
|
12
|
+
|
13
|
+
key = ''
|
14
|
+
bytes.each{ |b| key << encode_char(((b << 4) | (b >> 4)) & 0xff) }
|
15
|
+
key
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generates a lock between 80 and 134 random characters, and a pk of 16
|
19
|
+
# random characters. At least in theory, other clients were doing exactly
|
20
|
+
# this, so I just mirrored them.
|
21
|
+
def generate_lock
|
22
|
+
lock = 'EXTENDEDPROTOCOL'
|
23
|
+
[lock + ('ABC' * 6), 'ABCD' * 4]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Watch out for those special ones...
|
27
|
+
def encode_char c
|
28
|
+
if [0, 5, 36, 96, 124, 126].include? c
|
29
|
+
sprintf '/%%DCN%03d%%/', c
|
30
|
+
else
|
31
|
+
c.chr
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/fargo.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'active_support/dependencies/autoload'
|
4
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
5
|
+
require 'active_support/buffered_logger'
|
6
|
+
require 'active_support/concern'
|
7
|
+
|
8
|
+
module Fargo
|
9
|
+
extend ActiveSupport::Autoload
|
10
|
+
|
11
|
+
class ConnectionException < RuntimeError; end
|
12
|
+
|
13
|
+
mattr_accessor:logger
|
14
|
+
self.logger = ActiveSupport::BufferedLogger.new STDOUT
|
15
|
+
|
16
|
+
autoload :Utils
|
17
|
+
autoload :Publisher
|
18
|
+
autoload :Parser
|
19
|
+
autoload :Server
|
20
|
+
autoload :Client
|
21
|
+
autoload :Search
|
22
|
+
autoload :SearchResult
|
23
|
+
autoload :VERSION
|
24
|
+
|
25
|
+
module Supports
|
26
|
+
extend ActiveSupport::Autoload
|
27
|
+
|
28
|
+
autoload :Chat
|
29
|
+
autoload :Searches
|
30
|
+
autoload :NickList
|
31
|
+
autoload :Uploads
|
32
|
+
autoload :Downloads
|
33
|
+
autoload :Persistence
|
34
|
+
autoload :Timeout
|
35
|
+
autoload :FileList
|
36
|
+
end
|
37
|
+
|
38
|
+
module Connection
|
39
|
+
extend ActiveSupport::Autoload
|
40
|
+
|
41
|
+
autoload :Base
|
42
|
+
autoload :Download
|
43
|
+
autoload :Hub
|
44
|
+
autoload :Search
|
45
|
+
autoload :Upload
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fargo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Alex Crichton
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-09-06 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: activesupport
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 3
|
29
|
+
- 0
|
30
|
+
- 0
|
31
|
+
version: 3.0.0
|
32
|
+
type: :runtime
|
33
|
+
prerelease: false
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: libxml-ruby
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: bzip2-ruby
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
type: :runtime
|
59
|
+
prerelease: false
|
60
|
+
version_requirements: *id003
|
61
|
+
description: DC Client
|
62
|
+
email: alex@alexcrichton.com
|
63
|
+
executables: []
|
64
|
+
|
65
|
+
extensions: []
|
66
|
+
|
67
|
+
extra_rdoc_files: []
|
68
|
+
|
69
|
+
files:
|
70
|
+
- lib/fargo.rb
|
71
|
+
- lib/fargo/client.rb
|
72
|
+
- lib/fargo/connection/base.rb
|
73
|
+
- lib/fargo/connection/download.rb
|
74
|
+
- lib/fargo/connection/hub.rb
|
75
|
+
- lib/fargo/connection/search.rb
|
76
|
+
- lib/fargo/connection/upload.rb
|
77
|
+
- lib/fargo/parser.rb
|
78
|
+
- lib/fargo/publisher.rb
|
79
|
+
- lib/fargo/search.rb
|
80
|
+
- lib/fargo/search_result.rb
|
81
|
+
- lib/fargo/server.rb
|
82
|
+
- lib/fargo/supports/chat.rb
|
83
|
+
- lib/fargo/supports/downloads.rb
|
84
|
+
- lib/fargo/supports/file_list.rb
|
85
|
+
- lib/fargo/supports/nick_list.rb
|
86
|
+
- lib/fargo/supports/persistence.rb
|
87
|
+
- lib/fargo/supports/searches.rb
|
88
|
+
- lib/fargo/supports/timeout.rb
|
89
|
+
- lib/fargo/supports/uploads.rb
|
90
|
+
- lib/fargo/utils.rb
|
91
|
+
- lib/fargo/version.rb
|
92
|
+
has_rdoc: true
|
93
|
+
homepage: http://github.com/alexcrichton/fargo
|
94
|
+
licenses: []
|
95
|
+
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options:
|
98
|
+
- --charset=UTF-8
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
hash: 1373483426740815041
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
hash: 1373483426740815041
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
119
|
+
requirements: []
|
120
|
+
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.3.7
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: A client for the DC protocol
|
126
|
+
test_files: []
|
127
|
+
|