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,139 @@
1
+ module Cancer
2
+ module Spec
3
+
4
+ class MockAdapter < Cancer::Adapter
5
+ class << self
6
+ attr_accessor :transcript, :instance, :dialog
7
+ end
8
+
9
+ def self.mock(&dialog)
10
+ mock = Class.new(self)
11
+ mock.dialog = dialog
12
+ mock
13
+ end
14
+
15
+ def self.should_produce_transcript(&proc)
16
+ should Cancer::Spec::Matcher.new(Cancer::Spec::Transcript.new(&proc))
17
+ rescue ::Spec::Expectations::ExpectationNotMetError => e
18
+ if $cancer_error
19
+ if $cancer_error.backtrace and $cancer_error != e
20
+ e.backtrace.clear
21
+ e.backtrace.concat($cancer_error.backtrace)
22
+ end
23
+ $cancer_error = nil
24
+ end
25
+ if !Cancer::Spec.debug
26
+ backtrace = e.backtrace.dup
27
+ e.backtrace.clear
28
+ e.backtrace.concat backtrace.select { |f| !f.include?('spec_helper.rb') }
29
+ end
30
+ raise e
31
+ end
32
+
33
+ def initialize(stream, options={})
34
+ @stream, @options = stream, options
35
+ self.class.instance = self
36
+ @transcript = self.class.transcript.dup
37
+ @queue = Queue.new
38
+ @responder_tread = Thread.new do
39
+ Thread.current.abort_on_exception = true
40
+ begin
41
+ run_server_emulation_and_state_verification
42
+ loop do
43
+ data = @queue.pop
44
+ break unless data
45
+ verify_client_data(data)
46
+ run_server_emulation_and_state_verification
47
+ end
48
+ rescue ::Spec::Expectations::ExpectationNotMetError => e
49
+ if lock = @stream.instance_variable_get('@lock')
50
+ $cancer_error ||= e
51
+ lock.continue!(e)
52
+ elsif locks = @stream.instance_variable_get('@receive_locks') and lock = locks.first
53
+ $cancer_error ||= e
54
+ lock.continue!(e)
55
+ else
56
+ $cancer_error ||= e
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def connect
63
+ end
64
+
65
+ def start_tls?
66
+ end
67
+
68
+ def start_tls
69
+ end
70
+
71
+ def disconnect
72
+ @queue.push(nil)
73
+ @responder_tread.join
74
+
75
+ if $cancer_error
76
+ raise $cancer_error
77
+ elsif @transcript.first
78
+ messages = ""
79
+ backtrace = nil
80
+ while message = @transcript.pop
81
+ case message.first
82
+ when :in then messages += " in: #{message[1]}\n"
83
+ when :out then messages += " out: #{message[1]}\n"
84
+ when :verify then messages += " verify: #{(message[2] || message[3]).first}\n"
85
+ end
86
+ backtrace ||= message.last
87
+ end
88
+ raise Cancer::Spec::Error.new("Expected a longer dialog:\n#{messages}", backtrace)
89
+ end
90
+ end
91
+
92
+ def send(data)
93
+ @queue.push(data)
94
+ end
95
+
96
+ def verify_client_data(xml)
97
+ type, data, backtrace = @transcript.pop
98
+
99
+ if type == nil
100
+ raise Cancer::Spec::Error.new("Unexpected data send by client:\n #{xml}")
101
+ end
102
+
103
+ if type != :out
104
+ raise Cancer::Spec::Error.new("should be write instead of #{type}", backtrace)
105
+ end
106
+
107
+ a = (Nokogiri::XML.parse(data).root rescue data) || data
108
+ b = (Nokogiri::XML.parse(xml).root rescue xml) || xml
109
+
110
+ a.strip_empty_text! if Nokogiri::XML::Node === a
111
+
112
+ if Nokogiri::XML::Node === b and Nokogiri::XML::Node === a
113
+ if !b.matching?(a)
114
+ raise Cancer::Spec::Error.new("Expected xml-data doesn't match the actual xml-data!\n expected: #{data}\n actual: #{xml}", backtrace)
115
+ end
116
+ elsif a != b
117
+ raise Cancer::Spec::Error.new("Expected xml-data doesn't match the actual xml-data!\n expected: #{data}\n actual: #{xml}", backtrace)
118
+ end
119
+ end
120
+
121
+ def run_server_emulation_and_state_verification
122
+ while @transcript.first and @transcript.first[0] != :out
123
+ type, data, backtrace = @transcript.pop
124
+ return if type == nil
125
+ case type
126
+ when :in
127
+ if Proc === data
128
+ @stream.received_data(stream.build(&data))
129
+ else
130
+ @stream.received_data(data)
131
+ end
132
+ when :verify then data.call(@stream)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,15 @@
1
+ module Cancer
2
+ module Spec
3
+
4
+ class MockStream < Cancer::Spec::MockAdapter
5
+ def self.should_produce_transcript
6
+ super() do |t|
7
+ t.initiate_stream('alice@wonderland.lit')
8
+ yield(t)
9
+ t.close_stream
10
+ end
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,107 @@
1
+ module Cancer
2
+ module Spec
3
+
4
+ class Transcript
5
+
6
+ def initialize(entries=nil)
7
+ @entries = entries || []
8
+ yield(self) if block_given?
9
+ end
10
+
11
+ def dup
12
+ self.class.new(@entries.dup)
13
+ end
14
+
15
+ def in(xml, backtrace=nil)
16
+ @entries.push [:in, xml, backtrace||caller]
17
+ end
18
+
19
+ def out(xml, backtrace=nil)
20
+ @entries.push [:out, xml, backtrace||caller]
21
+ end
22
+
23
+ def verify(&proc)
24
+ @entries.push [:verify, proc, nil, caller]
25
+ end
26
+
27
+ def should(matcher)
28
+ @entries.push [:verify, lambda { |stream| stream.should matcher }, caller]
29
+ end
30
+
31
+ def should_not(matcher)
32
+ @entries.push [:verify, lambda { |stream| stream.should_not matcher }, caller]
33
+ end
34
+
35
+ def start_stream(from, to=nil, id=nil, backtrace=nil)
36
+ backtrace = backtrace || caller
37
+ from = from.to_jid
38
+ to ||= from.server_jid
39
+ id ||= rand(6000).to_s
40
+ self.out %{<stream:stream xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" to="#{to}" from="#{from.bare_jid}" version="1.0">}, backtrace
41
+ self.in %{<stream:stream xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" from="to" to="#{from.bare_jid}" id="#{id}" version="1.0">}, backtrace
42
+ end
43
+
44
+ def close_stream(backtrace=nil)
45
+ backtrace = backtrace || caller
46
+ self.out %{</stream:stream>}, backtrace
47
+ self.in %{</stream:stream>}, backtrace
48
+ end
49
+
50
+ def initiate_stream(from, to=nil, id=nil, backtrace=nil)
51
+ backtrace = backtrace || caller
52
+ from = from.to_jid
53
+
54
+ self.start_stream(from, to, id, backtrace)
55
+ self.in %{<stream:features>
56
+ <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls">
57
+ <optional/>
58
+ </starttls>
59
+ <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
60
+ <mechanism>PLAIN</mechanism>
61
+ <required/>
62
+ </mechanisms>
63
+ </stream:features>}, backtrace
64
+
65
+ self.out %{<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>}, backtrace
66
+ self.in %{<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>}, backtrace
67
+
68
+ self.start_stream(from, to, id, backtrace)
69
+ self.in %{<stream:features>
70
+ <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
71
+ <mechanism>PLAIN</mechanism>
72
+ <required/>
73
+ </mechanisms>
74
+ </stream:features>}, backtrace
75
+
76
+ self.out %{<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">AGFsaWNlQHdvbmRlcmxhbmQubGl0AGloYXRldGhlcXVlZW4=</auth>}, backtrace
77
+ self.in %{<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl" />}, backtrace
78
+
79
+ self.start_stream(from, to, id, backtrace)
80
+ self.in %{<stream:features>
81
+ <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
82
+ <required/>
83
+ </bind>
84
+ <session xmlns="urn:ietf:params:xml:ns:xmpp-session">
85
+ <optional/>
86
+ </session>
87
+ </stream:features>}, backtrace
88
+
89
+ self.out %{<iq type="set" id="cancer-1"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/></iq>}, backtrace
90
+ self.in %{<iq type="result" id="cancer-1"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>#{from.bare_jid}/#{from.resource || 'rabbithole'}</jid></bind></iq>}, backtrace
91
+
92
+ self.out %{<iq type="set" id="cancer-2"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>}, backtrace
93
+ self.in %{<iq type="result" id="cancer-2" />}, backtrace
94
+
95
+ end
96
+
97
+ def first
98
+ @entries.first
99
+ end
100
+
101
+ def pop
102
+ @entries.shift
103
+ end
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,182 @@
1
+
2
+ module Cancer
3
+ class Stream
4
+
5
+ autoload :XEP, 'cancer/stream/xep'
6
+ autoload :Builder, 'cancer/stream/builder'
7
+ autoload :Controller, 'cancer/stream/controller'
8
+ autoload :EventHelper, 'cancer/stream/event_helper'
9
+
10
+ include Cancer::SynchronizedStanza
11
+ include Cancer::Stream::Controller
12
+ include Cancer::Stream::Builder
13
+ include Cancer::Stream::XEP
14
+ include Cancer::Stream::EventHelper
15
+ extend Cancer::Support
16
+ include Cancer::Events::Eventable
17
+
18
+ attr_accessor :options
19
+
20
+ def self.open(options={},&proc)
21
+ new(options).open(&proc)
22
+ end
23
+
24
+ def initialize(options={})
25
+
26
+ options[:jid] = options[:jid].to_jid if options[:jid]
27
+ options[:adapter] ||= Cancer::Adapters::Socket
28
+
29
+ @options = options
30
+ @lock = Cancer::Lock.new
31
+ @receive_locks = []
32
+ @mutex = Mutex.new
33
+
34
+ register_with Cancer::Events::Manager.new
35
+ @low_priority_queue = Cancer::ThreadPool.start!(4) do |job|
36
+ args, proc = *job
37
+ proc.call(*args)
38
+ end
39
+
40
+ @stream_parser = Cancer::StreamParser.new(self)
41
+ @xml_parser = Nokogiri::XML::SAX::PushParser.new(@stream_parser)
42
+
43
+ ['core', options[:xeps]].flatten.compact.each do |xep_name|
44
+ install_xep xep_name
45
+ end
46
+
47
+ enhance_stream_with_xeps!
48
+ @manager.resolve_martchers!
49
+
50
+ @initialized = true
51
+
52
+ controller do
53
+ on { |e| e.exception(Exception) }
54
+ def err(e)
55
+ Cancer.logger.error e.exception
56
+ end
57
+ end
58
+
59
+ @exception = nil
60
+ end
61
+
62
+ def deferred(*args, &proc)
63
+ @manager.deferred(*args, &proc)
64
+ end
65
+
66
+ def enqueue(*args, &proc)
67
+ @low_priority_queue.push([args, proc])
68
+ end
69
+
70
+ def fire!(*args)
71
+ @manager.fire!(*args)
72
+ end
73
+
74
+ def handle_interthread_exceptions
75
+ raise @exception if @exception
76
+ end
77
+
78
+ def extend_stream(&proc)
79
+ metaklass = (class << self ; self ; end)
80
+ metaklass.instance_eval(&proc)
81
+ end
82
+
83
+ def send(stanza=nil, &proc)
84
+ handle_interthread_exceptions
85
+ stanza ||= proc
86
+ if Proc === stanza
87
+ send build(&stanza)
88
+ else
89
+ send_data stanza.to_s.gsub(/>[\n\s]+/m, '>').gsub(/[\n\s]+</m, '<')
90
+ end
91
+ end
92
+
93
+ def received_data(data)
94
+ data = process_received_data(data)
95
+ @xml_parser << data
96
+
97
+ rescue RuntimeError => e
98
+ if data[0, 5] == "<?xml" or data[0, 14] == "<stream:stream"
99
+ @xml_parser = Nokogiri::XML::SAX::PushParser.new(@stream_parser)
100
+ @xml_parser << data
101
+ else
102
+ raise e
103
+ end
104
+ end
105
+
106
+ def received_xml(xml)
107
+ Cancer.logger.debug "RECEIVED: #{xml}"
108
+ handle_stream_errors(xml) or
109
+ handle_synchronized_stanza(xml) or
110
+ @manager.fire!(Cancer::Events::XMLEvent, xml)
111
+ end
112
+
113
+ def handle_stream_errors(xml)
114
+ return unless xml.named?('error', STREAM_NS)
115
+
116
+ @exception = Cancer::StreamError.new(xml)
117
+ handle_synchronized_stanza(@exception)
118
+ if @lock
119
+ @lock.continue!(@exception)
120
+ end
121
+
122
+ true
123
+ end
124
+
125
+ def open
126
+ handle_interthread_exceptions
127
+
128
+ resolve_options(options)
129
+
130
+ @manager.start!
131
+ @adapter = options[:adapter].new(self, @options)
132
+ @adapter.connect
133
+ @manager.fire!(Cancer::Events::NamedEvent, :connected)
134
+
135
+ send_stream_header
136
+ @lock.wait!
137
+ @lock = nil
138
+ if block_given?
139
+ yield(self)
140
+ close
141
+ end
142
+ self
143
+ end
144
+
145
+ def close
146
+ handle_interthread_exceptions
147
+
148
+ @low_priority_queue.stop!
149
+
150
+ @adapter.send('</stream:stream>')
151
+ @adapter.disconnect
152
+
153
+ @manager.stop!
154
+
155
+ self
156
+ end
157
+
158
+ def initialization_completed!
159
+ @lock.continue!
160
+ end
161
+
162
+ private
163
+
164
+ def resolve_options(options)
165
+ options[:username] ||= options[:jid].user
166
+ end
167
+
168
+ def process_received_data(data)
169
+ data
170
+ end
171
+
172
+ def process_send_data(data)
173
+ data
174
+ end
175
+
176
+ def send_data(data)
177
+ Cancer.logger.debug "SEND: #{data.to_s}"
178
+ @adapter.send(process_send_data(data))
179
+ end
180
+
181
+ end
182
+ end
@@ -0,0 +1,20 @@
1
+
2
+ module Cancer
3
+ class Stream
4
+ module Builder
5
+
6
+ def builder_helpers
7
+ @builder_helpers ||= Module.new
8
+ end
9
+
10
+ def extend_builder(&proc)
11
+ self.builder_helpers.module_eval(&proc)
12
+ end
13
+
14
+ def build(builder=nil, &proc)
15
+ Cancer::Builder.new(self, builder, self.builder_helpers, &proc).doc.root
16
+ end
17
+
18
+ end
19
+ end
20
+ end