cancer 0.1.0.a1

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