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.
Files changed (89) hide show
  1. data/.autotest +3 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE.txt +22 -0
  4. data/Rakefile +46 -0
  5. data/VERSION +1 -0
  6. data/cancer.gemspec +156 -0
  7. data/documentation/STREAM_INITIATION.markdown +18 -0
  8. data/examples/example.rb +80 -0
  9. data/examples/example_2.rb +20 -0
  10. data/examples/example_em.rb +11 -0
  11. data/examples/example_em_helper.rb +23 -0
  12. data/examples/example_im_roster.rb +26 -0
  13. data/examples/example_xep_0004.rb +124 -0
  14. data/examples/example_xep_0047.rb +35 -0
  15. data/examples/example_xep_0050.rb +78 -0
  16. data/examples/example_xep_0065.rb +66 -0
  17. data/examples/example_xep_0096.dup.rb +40 -0
  18. data/examples/example_xep_0096_xep_0047.rb +42 -0
  19. data/examples/example_xep_0096_xep_0065.rb +40 -0
  20. data/lib/cancer.rb +122 -0
  21. data/lib/cancer/adapter.rb +33 -0
  22. data/lib/cancer/adapters/em.rb +88 -0
  23. data/lib/cancer/adapters/socket.rb +122 -0
  24. data/lib/cancer/builder.rb +28 -0
  25. data/lib/cancer/controller.rb +32 -0
  26. data/lib/cancer/dependencies.rb +187 -0
  27. data/lib/cancer/events/abstract_event.rb +10 -0
  28. data/lib/cancer/events/base_matchers.rb +63 -0
  29. data/lib/cancer/events/binary_matchers.rb +30 -0
  30. data/lib/cancer/events/eventable.rb +92 -0
  31. data/lib/cancer/events/exception_events.rb +28 -0
  32. data/lib/cancer/events/handler.rb +33 -0
  33. data/lib/cancer/events/manager.rb +130 -0
  34. data/lib/cancer/events/named_events.rb +25 -0
  35. data/lib/cancer/events/proc_matcher.rb +16 -0
  36. data/lib/cancer/events/xml_events.rb +44 -0
  37. data/lib/cancer/exceptions.rb +53 -0
  38. data/lib/cancer/jid.rb +215 -0
  39. data/lib/cancer/lock.rb +32 -0
  40. data/lib/cancer/spec.rb +35 -0
  41. data/lib/cancer/spec/error.rb +18 -0
  42. data/lib/cancer/spec/extentions.rb +79 -0
  43. data/lib/cancer/spec/matcher.rb +30 -0
  44. data/lib/cancer/spec/mock_adapter.rb +139 -0
  45. data/lib/cancer/spec/mock_stream.rb +15 -0
  46. data/lib/cancer/spec/transcript.rb +107 -0
  47. data/lib/cancer/stream.rb +182 -0
  48. data/lib/cancer/stream/builder.rb +20 -0
  49. data/lib/cancer/stream/controller.rb +36 -0
  50. data/lib/cancer/stream/event_helper.rb +12 -0
  51. data/lib/cancer/stream/xep.rb +52 -0
  52. data/lib/cancer/stream_parser.rb +144 -0
  53. data/lib/cancer/support.rb +27 -0
  54. data/lib/cancer/support/hash.rb +32 -0
  55. data/lib/cancer/support/string.rb +22 -0
  56. data/lib/cancer/synchronized_stanza.rb +79 -0
  57. data/lib/cancer/thread_pool.rb +118 -0
  58. data/lib/cancer/xep.rb +43 -0
  59. data/lib/cancer/xeps/core.rb +109 -0
  60. data/lib/cancer/xeps/core/bind.rb +33 -0
  61. data/lib/cancer/xeps/core/sasl.rb +113 -0
  62. data/lib/cancer/xeps/core/session.rb +22 -0
  63. data/lib/cancer/xeps/core/stream.rb +18 -0
  64. data/lib/cancer/xeps/core/terminator.rb +21 -0
  65. data/lib/cancer/xeps/core/tls.rb +34 -0
  66. data/lib/cancer/xeps/im.rb +323 -0
  67. data/lib/cancer/xeps/xep_0004_x_data.rb +692 -0
  68. data/lib/cancer/xeps/xep_0020_feature_neg.rb +35 -0
  69. data/lib/cancer/xeps/xep_0030_disco.rb +167 -0
  70. data/lib/cancer/xeps/xep_0047_ibb.rb +322 -0
  71. data/lib/cancer/xeps/xep_0050_commands.rb +256 -0
  72. data/lib/cancer/xeps/xep_0065_bytestreams.rb +306 -0
  73. data/lib/cancer/xeps/xep_0066_oob.rb +69 -0
  74. data/lib/cancer/xeps/xep_0095_si.rb +211 -0
  75. data/lib/cancer/xeps/xep_0096_si_filetransfer.rb +173 -0
  76. data/lib/cancer/xeps/xep_0114_component.rb +73 -0
  77. data/lib/cancer/xeps/xep_0115_caps.rb +180 -0
  78. data/lib/cancer/xeps/xep_0138_compress.rb +134 -0
  79. data/lib/cancer/xeps/xep_0144_rosterx.rb +250 -0
  80. data/lib/cancer/xeps/xep_0184_receipts.rb +40 -0
  81. data/lib/cancer/xeps/xep_0199_ping.rb +41 -0
  82. data/spec/spec.opts +2 -0
  83. data/spec/spec_helper.rb +14 -0
  84. data/spec/stream/stanza_errors_spec.rb +47 -0
  85. data/spec/stream/stream_errors_spec.rb +38 -0
  86. data/spec/stream/stream_initialization_spec.rb +160 -0
  87. data/spec/xep_0050/local_spec.rb +165 -0
  88. data/spec/xep_0050/remote_spec.rb +44 -0
  89. 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
@@ -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
@@ -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
@@ -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