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.
@@ -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
@@ -0,0 +1,16 @@
1
+ module Fargo
2
+ module Supports
3
+ module Uploads
4
+ # TODO: write this module...
5
+
6
+ def open_slots
7
+ 0
8
+ end
9
+
10
+ def share_size
11
+ 5368709121 # 5 GB + 1 byte
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Fargo
2
+ VERSION = '0.1.0'
3
+ 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
+