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,53 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
|
4
|
+
class CancerError < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
class XMPPError < CancerError
|
8
|
+
attr_reader :conditions
|
9
|
+
def initialize(xml)
|
10
|
+
text = xml.first('n:text', "n" => 'urn:ietf:params:xml:ns:xmpp-streams')
|
11
|
+
text = text ? text.text.to_s : "Stream error: #{xml}"
|
12
|
+
super(text)
|
13
|
+
@conditions = xml.all('*[name() != "text"]')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class StreamError < XMPPError
|
18
|
+
end
|
19
|
+
|
20
|
+
class StanzaError < XMPPError
|
21
|
+
attr_reader :stanza
|
22
|
+
def initialize(xml)
|
23
|
+
super(xml.first('c:error'))
|
24
|
+
@stanza = xml
|
25
|
+
end
|
26
|
+
def self.build(xml)
|
27
|
+
type = xml.first('c:error')[:type]
|
28
|
+
case type
|
29
|
+
when 'cancel' then StanzaCancelError.new(xml)
|
30
|
+
when 'continue' then StanzaContinueError.new(xml)
|
31
|
+
when 'modify' then StanzaModifyError.new(xml)
|
32
|
+
when 'auth' then StanzaAuthError.new(xml)
|
33
|
+
when 'wait' then StanzaWaitError.new(xml)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class StanzaCancelError < StanzaError
|
39
|
+
end
|
40
|
+
|
41
|
+
class StanzaContinueError < StanzaError
|
42
|
+
end
|
43
|
+
|
44
|
+
class StanzaModifyError < StanzaError
|
45
|
+
end
|
46
|
+
|
47
|
+
class StanzaAuthError < StanzaError
|
48
|
+
end
|
49
|
+
|
50
|
+
class StanzaWaitError < StanzaError
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/cancer/jid.rb
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Cancer
|
5
|
+
|
6
|
+
#
|
7
|
+
# RFC2368, The mailto URL scheme
|
8
|
+
#
|
9
|
+
class JID < URI::Generic
|
10
|
+
include REGEXP
|
11
|
+
|
12
|
+
COMPONENT = [ :scheme, :user, :domain, :resource ].freeze
|
13
|
+
|
14
|
+
RESOURCE_PATTERN = "(?:[^\\/]*)".freeze
|
15
|
+
RESOURCE_REGEXP = Regexp.new(RESOURCE_PATTERN, 'N').freeze
|
16
|
+
USER_PATTERN = "(?:#{PATTERN::ESCAPED}|[^@])*".freeze
|
17
|
+
DOMAIN_PATTERN = "(?:#{PATTERN::ESCAPED}|[^\\/:])".freeze
|
18
|
+
FULL_JID_REGEXP = Regexp.new(" # :nodoc:
|
19
|
+
\\A
|
20
|
+
(?:
|
21
|
+
(#{USER_PATTERN})@ (?# 1: user)
|
22
|
+
)?
|
23
|
+
(#{DOMAIN_PATTERN}*?) (?# 2: domain)
|
24
|
+
(?:
|
25
|
+
\\/(#{RESOURCE_PATTERN}(?:\\/#{RESOURCE_PATTERN})*) (?# 3: resource)
|
26
|
+
)?
|
27
|
+
\\z
|
28
|
+
", Regexp::EXTENDED, 'N').freeze
|
29
|
+
|
30
|
+
def self.parse(string)
|
31
|
+
URI.parse(string[0,5] == 'xmpp:' ? string : 'xmpp:' + string)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.build(args)
|
35
|
+
tmp = Util::make_components_hash(self, args)
|
36
|
+
|
37
|
+
if tmp[:user]
|
38
|
+
tmp[:opaque] = tmp.delete(:user) + '@'
|
39
|
+
else
|
40
|
+
tmp[:opaque] = ''
|
41
|
+
end
|
42
|
+
|
43
|
+
if tmp[:domain]
|
44
|
+
tmp[:opaque] << tmp.delete(:domain)
|
45
|
+
else
|
46
|
+
tmp[:opaque] << ''
|
47
|
+
end
|
48
|
+
|
49
|
+
if tmp[:resource]
|
50
|
+
if tmp[:resource][0,1] == '/'
|
51
|
+
tmp[:opaque] << tmp.delete(:resource).to_s
|
52
|
+
else
|
53
|
+
tmp[:opaque] << '/' + tmp.delete(:resource).to_s
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
return super(tmp)
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize(*arg)
|
61
|
+
super(*arg)
|
62
|
+
|
63
|
+
if FULL_JID_REGEXP =~ @opaque
|
64
|
+
if arg[-1]
|
65
|
+
self.user = $1
|
66
|
+
self.domain = $2
|
67
|
+
self.resource = $3
|
68
|
+
else
|
69
|
+
set_user($1)
|
70
|
+
set_domain($2)
|
71
|
+
set_resource($3)
|
72
|
+
end
|
73
|
+
|
74
|
+
else
|
75
|
+
raise InvalidComponentError,
|
76
|
+
"unrecognised opaque part for xmpp url: #{@opaque}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_reader :domain, :user
|
81
|
+
attr_reader :resource
|
82
|
+
|
83
|
+
def check_user(v)
|
84
|
+
return true unless v
|
85
|
+
return true if v.size == 0
|
86
|
+
|
87
|
+
if OPAQUE !~ v || /\A#{USER_PATTERN}\z/o !~ v
|
88
|
+
raise InvalidComponentError,
|
89
|
+
"bad component(expected opaque component): #{v}"
|
90
|
+
end
|
91
|
+
|
92
|
+
return true
|
93
|
+
end
|
94
|
+
private :check_user
|
95
|
+
|
96
|
+
def set_user(v)
|
97
|
+
@user = v
|
98
|
+
end
|
99
|
+
protected :set_user
|
100
|
+
|
101
|
+
def user=(v)
|
102
|
+
check_user(v)
|
103
|
+
set_user(v)
|
104
|
+
v
|
105
|
+
end
|
106
|
+
|
107
|
+
def check_domain(v)
|
108
|
+
return true unless v
|
109
|
+
return true if v.size == 0
|
110
|
+
|
111
|
+
if OPAQUE !~ v || /\A#{DOMAIN_PATTERN}*\z/o !~ v
|
112
|
+
raise InvalidComponentError,
|
113
|
+
"bad component(expected opaque component): #{v}"
|
114
|
+
end
|
115
|
+
|
116
|
+
return true
|
117
|
+
end
|
118
|
+
private :check_domain
|
119
|
+
|
120
|
+
def set_domain(v)
|
121
|
+
@domain = v
|
122
|
+
end
|
123
|
+
protected :set_domain
|
124
|
+
|
125
|
+
def domain=(v)
|
126
|
+
check_domain(v)
|
127
|
+
set_domain(v)
|
128
|
+
v
|
129
|
+
end
|
130
|
+
|
131
|
+
def check_resource(v)
|
132
|
+
return true unless v
|
133
|
+
return true if v.size == 0
|
134
|
+
|
135
|
+
if OPAQUE !~ v ||
|
136
|
+
/\A\/?(#{RESOURCE_PATTERN}(?:\/#{RESOURCE_PATTERN})*)\z/o !~ v
|
137
|
+
raise InvalidComponentError,
|
138
|
+
"bad component(expected opaque component): #{v}"
|
139
|
+
end
|
140
|
+
|
141
|
+
return true
|
142
|
+
end
|
143
|
+
private :check_resource
|
144
|
+
|
145
|
+
def set_resource(v)
|
146
|
+
@resource = v
|
147
|
+
end
|
148
|
+
protected :set_resource
|
149
|
+
|
150
|
+
def resource=(v)
|
151
|
+
check_resource(v)
|
152
|
+
set_resource(v)
|
153
|
+
v
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_s(include_scheme=false)
|
157
|
+
(include_scheme ? @scheme + ':' : '') + (@user ? @user + '@' : '') + (@domain ? @domain : '') + (@resource ? '/' + @resource : '')
|
158
|
+
end
|
159
|
+
|
160
|
+
def server_jid!
|
161
|
+
self.resource = nil
|
162
|
+
self.user = nil
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
def server_jid
|
167
|
+
Cancer::JID.build([nil, @domain, nil])
|
168
|
+
end
|
169
|
+
|
170
|
+
def component_jid(name)
|
171
|
+
Cancer::JID.build([nil, (name + '.' + @domain), nil])
|
172
|
+
end
|
173
|
+
|
174
|
+
def bare_jid!
|
175
|
+
self.resource = nil
|
176
|
+
self
|
177
|
+
end
|
178
|
+
|
179
|
+
def bare_jid
|
180
|
+
Cancer::JID.build([@user, @domain, nil])
|
181
|
+
end
|
182
|
+
|
183
|
+
def merge!(options={})
|
184
|
+
self.user = options[:user] if options.key? :user
|
185
|
+
self.domain = options[:domain] if options.key? :domain
|
186
|
+
self.resource = options[:resource] if options.key? :resource
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
def merge(options={})
|
191
|
+
Cancer::JID.build([options[:user] || @user, options[:domain] || @domain, options[:resource] || @resource])
|
192
|
+
end
|
193
|
+
|
194
|
+
def to_jid
|
195
|
+
self
|
196
|
+
end
|
197
|
+
|
198
|
+
def to_hash
|
199
|
+
hash = { :user => @user, :domain => @domain, :resource => @resource }
|
200
|
+
hash.keys.each { |k| hash.delete(k) if hash[k].nil? }
|
201
|
+
hash
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
module URI
|
208
|
+
@@schemes['XMPP'] = Cancer::JID
|
209
|
+
end
|
210
|
+
|
211
|
+
class String
|
212
|
+
def to_jid
|
213
|
+
Cancer::JID.parse(self) rescue nil
|
214
|
+
end
|
215
|
+
end
|
data/lib/cancer/lock.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
class Lock
|
4
|
+
|
5
|
+
def initialize(&proc)
|
6
|
+
@mutex = Mutex.new
|
7
|
+
@condition = ConditionVariable.new
|
8
|
+
@proc = proc
|
9
|
+
@values = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def wait!
|
13
|
+
@mutex.synchronize do
|
14
|
+
@condition.wait(@mutex)
|
15
|
+
if @values.size == 1 and Exception === @values.first
|
16
|
+
raise @values.first
|
17
|
+
else
|
18
|
+
@proc.call(*@values) if @proc
|
19
|
+
return *@values
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def continue!(*values)
|
25
|
+
@mutex.synchronize do
|
26
|
+
@values = values
|
27
|
+
@condition.signal
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
data/lib/cancer/spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
require 'cancer/spec/extentions'
|
3
|
+
|
4
|
+
module Cancer
|
5
|
+
# A set of tools to test Cancer using rspec.
|
6
|
+
#
|
7
|
+
# Spec::Runner.configure do |config|
|
8
|
+
# config.include(Cancer::Spec)
|
9
|
+
# end
|
10
|
+
module Spec
|
11
|
+
|
12
|
+
autoload :Error, 'cancer/spec/error'
|
13
|
+
autoload :Matcher, 'cancer/spec/matcher'
|
14
|
+
autoload :MockStream, 'cancer/spec/mock_stream'
|
15
|
+
autoload :Transcript, 'cancer/spec/transcript'
|
16
|
+
autoload :MockAdapter, 'cancer/spec/mock_adapter'
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_accessor :debug
|
20
|
+
end
|
21
|
+
|
22
|
+
def dialog(&proc)
|
23
|
+
Cancer::Spec::MockAdapter.mock(&proc)
|
24
|
+
end
|
25
|
+
|
26
|
+
def dialog_with(options={}, &proc)
|
27
|
+
Cancer::Spec::MockStream.mock do |adapter|
|
28
|
+
options = {:jid => 'alice@wonderland.lit', :password => 'ihatethequeen'}.merge(options)
|
29
|
+
options = options.merge(:adapter => adapter)
|
30
|
+
Cancer::Stream.open(options, &proc)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Cancer
|
2
|
+
module Spec
|
3
|
+
|
4
|
+
class Error < ::Spec::Expectations::ExpectationNotMetError
|
5
|
+
attr_reader :error_message, :expectation_backtrace
|
6
|
+
def initialize(error_message, expectation_backtrace=nil)
|
7
|
+
@error_message, @expectation_backtrace = error_message, expectation_backtrace
|
8
|
+
end
|
9
|
+
def message
|
10
|
+
@error_message
|
11
|
+
end
|
12
|
+
def backtrace
|
13
|
+
@expectation_backtrace || super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
|
2
|
+
module Spec
|
3
|
+
module Matchers
|
4
|
+
class RaiseError #:nodoc:
|
5
|
+
def matches?(given_proc)
|
6
|
+
@raised_expected_error = false
|
7
|
+
@with_expected_message = false
|
8
|
+
@eval_block = false
|
9
|
+
@eval_block_passed = false
|
10
|
+
begin
|
11
|
+
given_proc.call
|
12
|
+
rescue Spec::Expectations::ExpectationNotMetError => e
|
13
|
+
@eval_block_passed = true
|
14
|
+
@raised_expected_error = true
|
15
|
+
@with_expected_message = true
|
16
|
+
raise e
|
17
|
+
|
18
|
+
rescue @expected_error => @actual_error
|
19
|
+
@raised_expected_error = true
|
20
|
+
@with_expected_message = verify_message
|
21
|
+
rescue Exception => @actual_error
|
22
|
+
# This clause should be empty, but rcov will not report it as covered
|
23
|
+
# unless something (anything) is executed within the clause
|
24
|
+
rcov_error_report = "http://eigenclass.org/hiki.rb?rcov-0.8.0"
|
25
|
+
end
|
26
|
+
|
27
|
+
unless negative_expectation?
|
28
|
+
eval_block if @raised_expected_error && @with_expected_message && @block
|
29
|
+
end
|
30
|
+
ensure
|
31
|
+
return (@raised_expected_error & @with_expected_message) ? (@eval_block ? @eval_block_passed : true) : false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module Nokogiri
|
38
|
+
module XML
|
39
|
+
class Node
|
40
|
+
def strip_empty_text!
|
41
|
+
if node_type == ELEMENT_NODE
|
42
|
+
self.children.each do |child|
|
43
|
+
child.strip_empty_text!
|
44
|
+
end
|
45
|
+
elsif node_type == TEXT_NODE
|
46
|
+
if to_s =~ /^[\s\n]+$/m
|
47
|
+
unlink
|
48
|
+
else
|
49
|
+
self.replace(Text.new(self.to_s.strip, self.document))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
self
|
53
|
+
end
|
54
|
+
def matching?(other)
|
55
|
+
(self.node_type == other.node_type) and
|
56
|
+
(
|
57
|
+
(
|
58
|
+
(self.node_type == ELEMENT_NODE) and
|
59
|
+
(self.name == other.name) and
|
60
|
+
((!!self.namespace) == (!!other.namespace)) and
|
61
|
+
(self.namespace ? (self.namespace.href == other.namespace.href) : true) and
|
62
|
+
(self.attributes.size == other.attributes.size) and
|
63
|
+
(self.attributes.inject(other.attributes) { |m, (x, n)|
|
64
|
+
m.delete(n.name) if m[n.name] and m[n.name].to_s == n.to_s; m }.empty?) and
|
65
|
+
(self.children.size == other.children.size) and
|
66
|
+
(self.children.inject(other.children.to_a) { |m, n|
|
67
|
+
m.shift if n.matching? m.first ; m }.empty?)
|
68
|
+
) or (
|
69
|
+
(self.node_type == TEXT_NODE) and
|
70
|
+
(self.to_s == other.to_s)
|
71
|
+
) or (
|
72
|
+
(self.node_type == CDATA_SECTION_NODE) and
|
73
|
+
(self.to_s == other.to_s)
|
74
|
+
)
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Cancer
|
2
|
+
module Spec
|
3
|
+
|
4
|
+
class Matcher
|
5
|
+
def initialize(transcript)
|
6
|
+
@transcript = transcript
|
7
|
+
end
|
8
|
+
def matches?(mock)
|
9
|
+
mock.transcript = @transcript.dup
|
10
|
+
mock.dialog.call(mock)
|
11
|
+
|
12
|
+
if $cancer_error
|
13
|
+
raise $cancer_error
|
14
|
+
$cancer_error = nil
|
15
|
+
end
|
16
|
+
return true
|
17
|
+
rescue ::Spec::Expectations::ExpectationNotMetError => e
|
18
|
+
@cancer_error ||= e
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
def failure_message_for_should
|
22
|
+
@cancer_error.message
|
23
|
+
end
|
24
|
+
def failure_message_for_should_not
|
25
|
+
@cancer_error.message
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|