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,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