cancer 0.1.0.a1
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/.autotest +3 -0
- data/.gitignore +5 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/cancer.gemspec +156 -0
- data/documentation/STREAM_INITIATION.markdown +18 -0
- data/examples/example.rb +80 -0
- data/examples/example_2.rb +20 -0
- data/examples/example_em.rb +11 -0
- data/examples/example_em_helper.rb +23 -0
- data/examples/example_im_roster.rb +26 -0
- data/examples/example_xep_0004.rb +124 -0
- data/examples/example_xep_0047.rb +35 -0
- data/examples/example_xep_0050.rb +78 -0
- data/examples/example_xep_0065.rb +66 -0
- data/examples/example_xep_0096.dup.rb +40 -0
- data/examples/example_xep_0096_xep_0047.rb +42 -0
- data/examples/example_xep_0096_xep_0065.rb +40 -0
- data/lib/cancer.rb +122 -0
- data/lib/cancer/adapter.rb +33 -0
- data/lib/cancer/adapters/em.rb +88 -0
- data/lib/cancer/adapters/socket.rb +122 -0
- data/lib/cancer/builder.rb +28 -0
- data/lib/cancer/controller.rb +32 -0
- data/lib/cancer/dependencies.rb +187 -0
- data/lib/cancer/events/abstract_event.rb +10 -0
- data/lib/cancer/events/base_matchers.rb +63 -0
- data/lib/cancer/events/binary_matchers.rb +30 -0
- data/lib/cancer/events/eventable.rb +92 -0
- data/lib/cancer/events/exception_events.rb +28 -0
- data/lib/cancer/events/handler.rb +33 -0
- data/lib/cancer/events/manager.rb +130 -0
- data/lib/cancer/events/named_events.rb +25 -0
- data/lib/cancer/events/proc_matcher.rb +16 -0
- data/lib/cancer/events/xml_events.rb +44 -0
- data/lib/cancer/exceptions.rb +53 -0
- data/lib/cancer/jid.rb +215 -0
- data/lib/cancer/lock.rb +32 -0
- data/lib/cancer/spec.rb +35 -0
- data/lib/cancer/spec/error.rb +18 -0
- data/lib/cancer/spec/extentions.rb +79 -0
- data/lib/cancer/spec/matcher.rb +30 -0
- data/lib/cancer/spec/mock_adapter.rb +139 -0
- data/lib/cancer/spec/mock_stream.rb +15 -0
- data/lib/cancer/spec/transcript.rb +107 -0
- data/lib/cancer/stream.rb +182 -0
- data/lib/cancer/stream/builder.rb +20 -0
- data/lib/cancer/stream/controller.rb +36 -0
- data/lib/cancer/stream/event_helper.rb +12 -0
- data/lib/cancer/stream/xep.rb +52 -0
- data/lib/cancer/stream_parser.rb +144 -0
- data/lib/cancer/support.rb +27 -0
- data/lib/cancer/support/hash.rb +32 -0
- data/lib/cancer/support/string.rb +22 -0
- data/lib/cancer/synchronized_stanza.rb +79 -0
- data/lib/cancer/thread_pool.rb +118 -0
- data/lib/cancer/xep.rb +43 -0
- data/lib/cancer/xeps/core.rb +109 -0
- data/lib/cancer/xeps/core/bind.rb +33 -0
- data/lib/cancer/xeps/core/sasl.rb +113 -0
- data/lib/cancer/xeps/core/session.rb +22 -0
- data/lib/cancer/xeps/core/stream.rb +18 -0
- data/lib/cancer/xeps/core/terminator.rb +21 -0
- data/lib/cancer/xeps/core/tls.rb +34 -0
- data/lib/cancer/xeps/im.rb +323 -0
- data/lib/cancer/xeps/xep_0004_x_data.rb +692 -0
- data/lib/cancer/xeps/xep_0020_feature_neg.rb +35 -0
- data/lib/cancer/xeps/xep_0030_disco.rb +167 -0
- data/lib/cancer/xeps/xep_0047_ibb.rb +322 -0
- data/lib/cancer/xeps/xep_0050_commands.rb +256 -0
- data/lib/cancer/xeps/xep_0065_bytestreams.rb +306 -0
- data/lib/cancer/xeps/xep_0066_oob.rb +69 -0
- data/lib/cancer/xeps/xep_0095_si.rb +211 -0
- data/lib/cancer/xeps/xep_0096_si_filetransfer.rb +173 -0
- data/lib/cancer/xeps/xep_0114_component.rb +73 -0
- data/lib/cancer/xeps/xep_0115_caps.rb +180 -0
- data/lib/cancer/xeps/xep_0138_compress.rb +134 -0
- data/lib/cancer/xeps/xep_0144_rosterx.rb +250 -0
- data/lib/cancer/xeps/xep_0184_receipts.rb +40 -0
- data/lib/cancer/xeps/xep_0199_ping.rb +41 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/stream/stanza_errors_spec.rb +47 -0
- data/spec/stream/stream_errors_spec.rb +38 -0
- data/spec/stream/stream_initialization_spec.rb +160 -0
- data/spec/xep_0050/local_spec.rb +165 -0
- data/spec/xep_0050/remote_spec.rb +44 -0
- metadata +200 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'eventmachine'
|
4
|
+
require 'net/dns/resolver'
|
5
|
+
require 'net/dns/rr/srv'
|
6
|
+
|
7
|
+
module Cancer
|
8
|
+
module Adapters
|
9
|
+
class EM < Cancer::Adapter
|
10
|
+
|
11
|
+
attr_accessor :lock
|
12
|
+
|
13
|
+
def initialize(stream, options={})
|
14
|
+
super
|
15
|
+
@options[:port] = (@options[:port] || @options[:default_port] || 5222)
|
16
|
+
unless @options[:server]
|
17
|
+
servers = lookup_xmpp_servers_for(
|
18
|
+
@options[:jid].domain,
|
19
|
+
@options[:port])
|
20
|
+
@options[:server] = servers.first[0]
|
21
|
+
@options[:port] = servers.first[1]
|
22
|
+
end
|
23
|
+
@lock = Cancer::Lock.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def connect
|
27
|
+
@connection = EventMachine.connect(@options[:server], @options[:port], Connection, self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def send(data)
|
31
|
+
@connection.send_data(data.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
def received_data(data)
|
35
|
+
@stream.received_data(data)
|
36
|
+
end
|
37
|
+
|
38
|
+
def start_tls?
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_tls
|
43
|
+
EventMachine.defer { @connection.start_tls(:verify_peer => false) }
|
44
|
+
@lock.wait!
|
45
|
+
end
|
46
|
+
|
47
|
+
def disconnect
|
48
|
+
EventMachine.defer { @connection.close_connection }
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def lookup_xmpp_servers_for(domain, default_port)
|
54
|
+
servers = Net::DNS::Resolver.start("_xmpp-client._tcp.#{domain}.", Net::DNS::SRV).answer
|
55
|
+
|
56
|
+
servers.sort! do |a,b|
|
57
|
+
(a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight)
|
58
|
+
end
|
59
|
+
|
60
|
+
servers.collect! do |record|
|
61
|
+
[record.host.to_s, record.port.to_i, :match]
|
62
|
+
end
|
63
|
+
|
64
|
+
if servers.empty?
|
65
|
+
servers = [[domain, default_port, :fallback]]
|
66
|
+
end
|
67
|
+
|
68
|
+
servers
|
69
|
+
end
|
70
|
+
|
71
|
+
class Connection < EventMachine::Connection
|
72
|
+
def initialize(adapter)
|
73
|
+
@adapter = adapter
|
74
|
+
super()
|
75
|
+
end
|
76
|
+
|
77
|
+
def ssl_handshake_completed
|
78
|
+
@adapter.lock.continue!
|
79
|
+
end
|
80
|
+
|
81
|
+
def receive_data data
|
82
|
+
@adapter.received_data(data)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'socket'
|
4
|
+
require 'openssl/nonblock'
|
5
|
+
require 'net/dns/resolver'
|
6
|
+
require 'net/dns/rr/srv'
|
7
|
+
|
8
|
+
module Cancer
|
9
|
+
module Adapters
|
10
|
+
class Socket < Cancer::Adapter
|
11
|
+
|
12
|
+
def initialize(stream, options={})
|
13
|
+
super
|
14
|
+
@options[:port] = (@options[:port] || @options[:default_port] || 5222)
|
15
|
+
unless @options[:server]
|
16
|
+
servers = lookup_xmpp_servers_for(
|
17
|
+
@options[:jid].domain,
|
18
|
+
@options[:port])
|
19
|
+
@options[:server] = servers.first[0]
|
20
|
+
@options[:port] = servers.first[1]
|
21
|
+
end
|
22
|
+
@mutex = Mutex.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect
|
26
|
+
@socket = TCPSocket.open(@options[:server], @options[:port])
|
27
|
+
start_parsing
|
28
|
+
end
|
29
|
+
|
30
|
+
def start_tls?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def start_tls
|
35
|
+
@mutex.synchronize do
|
36
|
+
|
37
|
+
ssl_socket = OpenSSL::SSL::SSLSocket.new(@socket, OpenSSL::SSL::SSLContext.new)
|
38
|
+
|
39
|
+
class << ssl_socket
|
40
|
+
alias_method :recv_nonblock, :read_nonblock
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
ssl_socket.connect_nonblock
|
45
|
+
rescue OpenSSL::SSL::ReadAgain
|
46
|
+
IO.select([ssl_socket])
|
47
|
+
retry
|
48
|
+
rescue OpenSSL::SSL::WriteAgain
|
49
|
+
IO.select(nil, [ssl_socket])
|
50
|
+
retry
|
51
|
+
end
|
52
|
+
|
53
|
+
@normal_socket = @socket
|
54
|
+
@socket = ssl_socket
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def start_parsing
|
60
|
+
@parser_thread = Thread.new do
|
61
|
+
Thread.current.abort_on_exception = true
|
62
|
+
|
63
|
+
until Thread.current[:stop]
|
64
|
+
begin
|
65
|
+
socket = @mutex.synchronize { @socket }
|
66
|
+
data = socket.recv_nonblock(1024)
|
67
|
+
@stream.received_data(data)
|
68
|
+
rescue Errno::EAGAIN, OpenSSL::SSL::ReadAgain
|
69
|
+
unless IO.select([socket], nil, nil, 3)
|
70
|
+
begin
|
71
|
+
socket << " "
|
72
|
+
socket.flush
|
73
|
+
rescue
|
74
|
+
end
|
75
|
+
end
|
76
|
+
retry
|
77
|
+
rescue IOError => e
|
78
|
+
Cancer.logger.fatal(e)
|
79
|
+
break
|
80
|
+
rescue OpenSSL::SSL::SSLError => e
|
81
|
+
Cancer.logger.fatal(e)
|
82
|
+
break
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def disconnect
|
90
|
+
@parser_thread[:stop] = true
|
91
|
+
@socket.close
|
92
|
+
@parser_thread.join
|
93
|
+
end
|
94
|
+
|
95
|
+
def send(data)
|
96
|
+
@socket << data.to_s
|
97
|
+
@socket.flush
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def lookup_xmpp_servers_for(domain, default_port)
|
103
|
+
servers = Net::DNS::Resolver.start("_xmpp-client._tcp.#{domain}.", Net::DNS::SRV).answer
|
104
|
+
|
105
|
+
servers.sort! do |a,b|
|
106
|
+
(a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight)
|
107
|
+
end
|
108
|
+
|
109
|
+
servers.collect! do |record|
|
110
|
+
[record.host.to_s, record.port.to_i, :match]
|
111
|
+
end
|
112
|
+
|
113
|
+
if servers.empty?
|
114
|
+
servers = [[domain, default_port, :fallback]]
|
115
|
+
end
|
116
|
+
|
117
|
+
servers
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
module Cancer
|
5
|
+
class Builder < DelegateClass(Nokogiri::XML::Builder)
|
6
|
+
|
7
|
+
extend Cancer::Support
|
8
|
+
|
9
|
+
def initialize(stream, builder=nil, helpers=nil, &proc)
|
10
|
+
@stream = stream
|
11
|
+
|
12
|
+
if builder
|
13
|
+
@builder = builder
|
14
|
+
super(@builder)
|
15
|
+
extend helpers if helpers
|
16
|
+
proc.call(self) if proc
|
17
|
+
else
|
18
|
+
Nokogiri::XML::Builder.new do |builder|
|
19
|
+
@builder = builder
|
20
|
+
super(@builder)
|
21
|
+
extend helpers if helpers
|
22
|
+
proc.call(self) if proc
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
class Controller
|
4
|
+
include Cancer::Events::Eventable
|
5
|
+
extend Cancer::Support
|
6
|
+
|
7
|
+
def initialize(stream)
|
8
|
+
@stream = stream
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(m, *args, &proc)
|
12
|
+
if @stream.respond_to?(m)
|
13
|
+
@stream.__send__(m,*args,&proc)
|
14
|
+
else
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.method_missing(m, *args, &proc)
|
20
|
+
if Cancer::Stream.respond_to?(m)
|
21
|
+
Cancer::Stream.__send__(m,*args,&proc)
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def send(*args,&proc)
|
28
|
+
@stream.send(*args,&proc)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'tsort'
|
2
|
+
|
3
|
+
module Cancer
|
4
|
+
module Dependencies
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(Cancer::Dependencies::ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def dependency(node, options={})
|
13
|
+
if options[:weak]
|
14
|
+
if options[:reverse]
|
15
|
+
weak_reverse_dependecies.push(node)
|
16
|
+
else
|
17
|
+
weak_dependecies.push(node)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
if options[:reverse]
|
21
|
+
strong_reverse_dependecies.push(node)
|
22
|
+
else
|
23
|
+
strong_dependecies.push(node)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def strong_dependecies
|
29
|
+
@strong_dependecies ||= begin
|
30
|
+
if respond_to?(:superclass) and superclass.respond_to?(:strong_dependecies)
|
31
|
+
superclass.strong_dependecies.dup
|
32
|
+
else
|
33
|
+
[]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def weak_dependecies
|
39
|
+
@weak_dependecies ||= begin
|
40
|
+
if respond_to?(:superclass) and superclass.respond_to?(:weak_dependecies)
|
41
|
+
superclass.weak_dependecies.dup
|
42
|
+
else
|
43
|
+
[]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def strong_reverse_dependecies
|
49
|
+
@strong_reverse_dependecies ||= begin
|
50
|
+
if respond_to?(:superclass) and superclass.respond_to?(:strong_reverse_dependecies)
|
51
|
+
superclass.strong_reverse_dependecies.dup
|
52
|
+
else
|
53
|
+
[]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def weak_reverse_dependecies
|
59
|
+
@weak_reverse_dependecies ||= begin
|
60
|
+
if respond_to?(:superclass) and superclass.respond_to?(:weak_reverse_dependecies)
|
61
|
+
superclass.weak_reverse_dependecies.dup
|
62
|
+
else
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
class MissingDependency < RuntimeError
|
71
|
+
def initialize(name, dep)
|
72
|
+
super("Missing dependency #{dep} for #{name}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Resolver
|
77
|
+
|
78
|
+
def initialize(options={})
|
79
|
+
@options = { :allow_reverse => true }.merge(options)
|
80
|
+
@defined_nodes = {}
|
81
|
+
end
|
82
|
+
|
83
|
+
def on_missing_strong_dependency(&proc)
|
84
|
+
@dependency_finder = proc if proc
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_node(name, desc)
|
88
|
+
unless desc.ancestors.include?(Cancer::Dependencies)
|
89
|
+
raise ArgumentError, 'not a Cancer::Dependency'
|
90
|
+
end
|
91
|
+
|
92
|
+
if @resolving
|
93
|
+
@dependency_added = true
|
94
|
+
@unprocessed_nodes.push(name)
|
95
|
+
end
|
96
|
+
|
97
|
+
@defined_nodes[name] = [
|
98
|
+
desc.strong_dependecies.dup,
|
99
|
+
desc.strong_reverse_dependecies.dup,
|
100
|
+
desc.weak_dependecies.dup,
|
101
|
+
desc.weak_reverse_dependecies.dup
|
102
|
+
]
|
103
|
+
end
|
104
|
+
|
105
|
+
def sort(*enabled_nodes)
|
106
|
+
@resolving = true
|
107
|
+
|
108
|
+
enabled_nodes = enabled_nodes.flatten.compact
|
109
|
+
(@defined_nodes.keys - enabled_nodes).each do |name|
|
110
|
+
@defined_nodes.delete(name)
|
111
|
+
end
|
112
|
+
|
113
|
+
@unprocessed_nodes = @defined_nodes.keys.dup
|
114
|
+
while name = @unprocessed_nodes.shift
|
115
|
+
dependencies = @defined_nodes[name]
|
116
|
+
if dependencies.size == 4
|
117
|
+
|
118
|
+
dependencies[0].each do |dep|
|
119
|
+
unless @defined_nodes.key? dep
|
120
|
+
@dependency_added = false
|
121
|
+
@dependency_finder.call(dep) if @dependency_finder
|
122
|
+
raise MissingDependency.new(name, dep) unless @dependency_added
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
if @options[:allow_reverse]
|
127
|
+
dependencies[1].each do |dep|
|
128
|
+
if node = @defined_nodes[dep]
|
129
|
+
node[0].push name
|
130
|
+
else
|
131
|
+
@dependency_added = false
|
132
|
+
@dependency_finder.call(dep) if @dependency_finder
|
133
|
+
if @dependency_added and node = @defined_nodes[dep]
|
134
|
+
node[0].push name
|
135
|
+
else
|
136
|
+
raise MissingDependency.new(name, dep)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
@unprocessed_nodes = @defined_nodes.keys.dup
|
146
|
+
while name = @unprocessed_nodes.shift
|
147
|
+
dependencies = @defined_nodes[name]
|
148
|
+
if dependencies.size == 4
|
149
|
+
|
150
|
+
dependencies[2].each do |dep|
|
151
|
+
if @defined_nodes.key? dep
|
152
|
+
dependencies[0].push dep
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
if @options[:allow_reverse]
|
157
|
+
dependencies[3].each do |dep|
|
158
|
+
if node = @defined_nodes[dep]
|
159
|
+
node[0].push name
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
@unprocessed_nodes = nil
|
168
|
+
@resolving = false
|
169
|
+
tsort
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
include TSort
|
175
|
+
|
176
|
+
def tsort_each_node(&proc)
|
177
|
+
@defined_nodes.each_key(&proc)
|
178
|
+
end
|
179
|
+
|
180
|
+
def tsort_each_child(node, &block)
|
181
|
+
@defined_nodes[node][0].each(&block)
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|