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