fargo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+