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,256 @@
1
+
2
+ module Cancer
3
+ # Ad-Hoc Commands
4
+ # http://xmpp.org/extensions/xep-0050.html
5
+ module XEP_0050
6
+ include Cancer::XEP
7
+
8
+ NS = "http://jabber.org/protocol/commands"
9
+ NAMESPACES = { 'c' => CLIENT_NS, 'cmd' => NS, 'x' => Cancer::XEP_0004::NS }
10
+
11
+ dependency 'core'
12
+ dependency 'xep-0004'
13
+ dependency 'xep-0030'
14
+
15
+ def self.enhance_stream(stream)
16
+ stream.extend_stream do
17
+ include Cancer::XEP_0050::StreamHelpers
18
+ end
19
+ stream.install_controller Cancer::XEP_0050::Controller
20
+ stream.install_event_helper Cancer::XEP_0050::EventDSL
21
+ stream.disco.feature(NS)
22
+ end
23
+
24
+ module StreamHelpers
25
+
26
+ def commands_provided_by(peer)
27
+ disco_items_for(peer, NS).inject({}) { |commands, item| commands[item['node'].to_s] = item['name'].to_s ; commands }
28
+ end
29
+
30
+ def command(to, node)
31
+ session = RemoteSession.new(self, to, node)
32
+ yield(session) if block_given?
33
+ session
34
+ end
35
+
36
+ def register_command(uri, name=nil)
37
+ @disco_commands ||= disco.item(:node => NS, :jid => self.options[:jid])
38
+ @disco_commands.item(:node => uri, :name => name, :jid => self.options[:jid])
39
+ end
40
+
41
+ end
42
+
43
+ class RemoteSession
44
+ attr_reader :peer, :node, :id, :status, :allowed_actions, :execute_action
45
+
46
+ def initialize(stream, peer, node)
47
+ @stream, @peer, @node = stream, peer, node
48
+ @status = @id = nil
49
+ @allowed_actions = [:execute]
50
+ @execute_action = :execute
51
+ @stages = []
52
+ end
53
+
54
+ def send(action, form=nil)
55
+ unless @allowed_actions.include?(action.to_sym)
56
+ raise "Action #{action} is not allowed (#{@allowed_actions.join(', ')})"
57
+ end
58
+
59
+ attributes = {:xmlns => NS, :node => @node, :action => action}
60
+ attributes[:sessionid] = @id if @id
61
+ result = @stream.send_iq(@peer, :set) do |x|
62
+ x.command(attributes) do
63
+ x.x(form) if form
64
+ end
65
+ end
66
+
67
+ command_node = result.first('/c:iq/cmd:command', NAMESPACES)
68
+ actions_node = command_node.all('./cmd:actions', NAMESPACES)
69
+ form_node = command_node.all('./x:x', NAMESPACES)
70
+
71
+ @id = command_node[:sessionid].to_s if command_node[:sessionid]
72
+ @status = (command_node[:status] || :completed).to_sym
73
+ @allowed_actions = actions_node.children.collect { |n| n.name.to_sym }
74
+ @execute_action = (actions_node[:execute] || :next).to_sym
75
+
76
+ form = Cancer::XEP_0004::Form.load(form_node)
77
+ @stages << form
78
+
79
+ form
80
+ end
81
+
82
+ def execute!(form=nil)
83
+ send(@execute_action, form)
84
+ end
85
+
86
+ def next!(form=nil)
87
+ send(:next, form)
88
+ end
89
+
90
+ def complete!(form=nil)
91
+ send(:complete, form)
92
+ end
93
+
94
+ def prev!
95
+ send(:prev)
96
+ end
97
+
98
+ def cancel!
99
+ send(:cancel)
100
+ end
101
+ end
102
+
103
+ class LocalSession
104
+ attr_reader :peer, :node, :id, :current_stage, :stages
105
+ attr_accessor :next_stage, :iq_id
106
+
107
+ def initialize(controller, peer, node, id, initial_stage=:initial)
108
+ @controller = controller
109
+ @peer, @node, @id = peer, node, id
110
+ @current_stage = nil
111
+ @next_stage = initial_stage.to_sym
112
+ @stages = {}
113
+ @iq_id = nil
114
+ end
115
+
116
+ def complete!
117
+ @controller.send_iq(@peer, :result, @iq_id) do |x|
118
+ x.command(:xmlns => NS, :node => @node, :sessionid => @id, :status => 'completed') do
119
+ yield x
120
+ end
121
+ end
122
+ end
123
+
124
+ def next!(next_stage, form=nil, options={})
125
+ options, form = form, nil if Hash === form
126
+
127
+ @next_stage = next_stage.to_sym
128
+ @controller.send_iq(@peer, :result, @iq_id) do |x|
129
+ x.command(:xmlns => NS, :node => @node, :sessionid => @id, :status => 'executing') do
130
+
131
+ x.actions(options[:execute] ? {:execute => options[:execute]} : {}) do
132
+ (options[:actions] || []).each do |action|
133
+ x.send(action)
134
+ end
135
+ end
136
+
137
+ if form
138
+ form = case form
139
+ when Cancer::XEP_0004::Form then form
140
+ when Cancer::XEP_0004::FormDefinition then form
141
+ else @controller.form(form)
142
+ end
143
+
144
+ x.x(form, options.merge(:verbose => true))
145
+ end
146
+
147
+ end
148
+ end
149
+ end
150
+
151
+ def form
152
+ @stages[@current_stage]
153
+ end
154
+
155
+ def push_stage!(iq_id, form_data)
156
+ @iq_id = iq_id
157
+ @stages[@next_stage] = (form_data ? Cancer::XEP_0004::Form.load(form_data) : nil)
158
+ @current_stage = @next_stage
159
+ @next_stage = nil
160
+ end
161
+ end
162
+
163
+ class Controller < Cancer::Controller
164
+
165
+ on { |e| e.xpath('/c:iq[@type="set"]/cmd:command', NAMESPACES) }
166
+ def handle_command(e)
167
+ command_node = e.xml.first('/c:iq/cmd:command', NAMESPACES)
168
+ session_id = (command_node[:sessionid] || rand(1<<100)).to_s
169
+ session = command_sessions[session_id]
170
+
171
+ unless session
172
+ session = LocalSession.new(self, e.sender, command_node[:node].to_s, session_id)
173
+ command_sessions[session_id] = session
174
+ end
175
+
176
+ session.push_stage!(e.id, e.xml.first('/c:iq/cmd:command/x:x', NAMESPACES))
177
+
178
+ fire! Cancer::XEP_0050::Event, self, session, command_node[:action]
179
+ end
180
+
181
+ def command_sessions
182
+ @command_sessions ||= {}
183
+ end
184
+
185
+ end
186
+
187
+ class Event < Cancer::Events::AbstractEvent
188
+ attr_reader :session, :action
189
+ def initialize(controller, session, action)
190
+ @controller, @session, @action = controller, session, action
191
+ @action = @action.to_sym if @action
192
+ end
193
+ end
194
+
195
+ class EventMatcher < Cancer::Events::Matcher
196
+
197
+ def initialize(node, options={})
198
+ @node = node.to_s
199
+ @stage = (options.delete(:stage) || :initial).to_sym
200
+ @action = (options.delete(:action) || :execute).to_sym
201
+ @default_action = (options.delete(:default_action) || :execute).to_sym
202
+ end
203
+
204
+ def match?(event)
205
+ (
206
+ Cancer::XEP_0050::Event === event and
207
+ matching_node?(event) and
208
+ matching_stage?(event) and
209
+ matching_action?(event)
210
+ )
211
+ end
212
+
213
+ def matching_node?(event)
214
+ @node ? (@node == event.session.node) : true
215
+ end
216
+
217
+ def matching_stage?(event)
218
+ @stage ? (@stage == event.session.current_stage) : true
219
+ end
220
+
221
+ def matching_action?(event)
222
+ @action ? (@action == (event.action || @default_action)) : true
223
+ end
224
+
225
+ end
226
+
227
+ module EventDSL
228
+
229
+ def command(node, options={})
230
+ Cancer::XEP_0050::EventMatcher.new(node, options)
231
+ end
232
+
233
+ end
234
+
235
+ end
236
+ end
237
+
238
+ =begin
239
+ class Controller
240
+
241
+ on { |e| e.command(:config) }
242
+ def config_initial_stage(e)
243
+ if ok? current_stage
244
+ e.session.next!(:ask_details, 'my_form', default_values)
245
+ else
246
+ # error
247
+ end
248
+ end
249
+
250
+ on { |e| e.command(:config, :stage => :ask_details) }
251
+ def config_ask_details_stage(e)
252
+
253
+ end
254
+
255
+ end
256
+ =end
@@ -0,0 +1,306 @@
1
+
2
+ require 'ipaddr'
3
+ require 'digest/sha1'
4
+
5
+ module Cancer
6
+ # SOCKS5 Bytestreams
7
+ # http://xmpp.org/extensions/xep-0065.html
8
+ module XEP_0065
9
+ include Cancer::XEP
10
+
11
+ dependency 'core'
12
+ dependency 'xep-0030'
13
+ dependency 'xep-0095', :weak => true
14
+
15
+ NS = 'http://jabber.org/protocol/bytestreams'
16
+
17
+ def self.enhance_stream(stream)
18
+ stream.extend_stream do
19
+ include Cancer::XEP_0065::StreamHelpers
20
+ end
21
+ stream.install_controller Cancer::XEP_0065::Controller
22
+ stream.install_event_helper Cancer::XEP_0065::EventDSL
23
+ if stream.respond_to?(:register_stream_method)
24
+ stream.register_stream_method(Cancer::XEP_0065::StreamMethod, Cancer::XEP_0065::NS)
25
+ stream.install_controller Cancer::XEP_0065::SOCKS5Controller
26
+ end
27
+ stream.disco.feature NS
28
+ end
29
+
30
+ module StreamHelpers
31
+
32
+ def open_socks5_bytestream(to, sid=nil)
33
+ sid ||= rand(1<<100).to_s
34
+
35
+ streamhosts = discover_streamhosts
36
+
37
+ result = send_iq(to, :set) do |x|
38
+ x.query(:xmlns => Cancer::XEP_0065::NS, :sid => sid, :mode => 'tcp') do
39
+
40
+ streamhosts.each do |jid, options|
41
+ x.streamhost(options.merge(:jid => jid))
42
+ end
43
+
44
+ end
45
+ end
46
+
47
+ namespaces = { 'c' => CLIENT_NS, 's' => Cancer::XEP_0065::NS }
48
+ streamhost = result.first("/c:iq/s:query/s:streamhost-used", namespaces)
49
+ if streamhost
50
+ streamhost_jid = streamhost[:jid].to_s
51
+ streamhost = streamhosts[streamhost_jid]
52
+
53
+ address_base = "#{sid}#{result[:to]}#{result[:from]}"
54
+ address = Digest::SHA1.hexdigest(address_base)
55
+
56
+ socket = TCPSocket.new(streamhost[:host], streamhost[:port])
57
+ socket.extend Cancer::XEP_0065::TCPSocketHelper
58
+ socket.extend Cancer::XEP_0065::SOCKS_5
59
+ socket.auth
60
+ socket.connect_domain(address, 0)
61
+
62
+ send_iq(streamhost_jid, :set) do |x|
63
+ x.query(:xmlns => Cancer::XEP_0065::NS, :sid => sid) do
64
+ x.activate(result[:from].to_s)
65
+ end
66
+ end
67
+
68
+ socket.peer = result[:from].to_s
69
+ socket.stream_id = sid
70
+ socket.streamhost = streamhost_jid
71
+
72
+ if block_given?
73
+ yield(socket)
74
+ socket.close
75
+ else
76
+ socket
77
+ end
78
+ else
79
+ return false
80
+ end
81
+ end
82
+
83
+ def discover_streamhosts(server_jid=nil)
84
+ @streamhosts ||= {}
85
+ server_jid ||= options[:jid].server_jid
86
+ @streamhosts[server_jid.to_s] ||= begin
87
+ discover_socks5_proxies(server_jid).inject({}) do |streamhosts, proxy|
88
+ streamhosts.merge discover_address_for_socks5_proxy(proxy)
89
+ end
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def discover_socks5_proxies(server_jid)
96
+ proxies = []
97
+
98
+ disco_items_for(server_jid).each do |item|
99
+ info = disco_info_for(item['jid'].to_s)
100
+ has_identity = info.identities.any? { |identity| identity[:category] == 'proxy' }
101
+ has_feature = info.features.include?(Cancer::XEP_0065::NS)
102
+ proxies.push(item['jid'].to_s) if has_identity and has_feature
103
+ end
104
+
105
+ proxies
106
+ end
107
+
108
+ def discover_address_for_socks5_proxy(jid, sid=nil)
109
+ attributes = { :xmlns => Cancer::XEP_0065::NS }
110
+ attributes[:sid] = sid if sid
111
+ result = send_iq(jid) do |x|
112
+ x.query(attributes)
113
+ end
114
+
115
+ streamhosts = {}
116
+
117
+ namespaces = { 'c' => CLIENT_NS, 's' => Cancer::XEP_0065::NS }
118
+ result.all('/c:iq/s:query/s:streamhost', namespaces) do |streamhost|
119
+ streamhosts[streamhost[:jid].to_s] = {
120
+ :host => streamhost[:host],
121
+ :port => (streamhost[:port].to_i rescue 1080),
122
+ :zeroconf => streamhost[:zeroconf],
123
+ }.clean
124
+ end
125
+
126
+ streamhosts
127
+ end
128
+
129
+ end
130
+
131
+ class Controller < Cancer::Controller
132
+
133
+ NAMESPACES = { 'c' => CLIENT_NS, 'bs' => NS }
134
+
135
+ on { |e| e.xpath('/c:iq/bs:query/bs:streamhost', NAMESPACES) }
136
+ def open_bytestream(e)
137
+ query = e.xml.first('/c:iq/bs:query', NAMESPACES)
138
+
139
+ streamhosts = {}
140
+ e.xml.all('/c:iq/bs:query/bs:streamhost', NAMESPACES) do |streamhost|
141
+ streamhosts[streamhost[:jid].to_s] = {
142
+ :host => streamhost[:host],
143
+ :port => (streamhost[:port].to_i rescue 1080),
144
+ :zeroconf => streamhost[:zeroconf],
145
+ }.clean
146
+ end
147
+
148
+ fire!(Event, self,
149
+ :streamhosts => streamhosts,
150
+ :iq_id => e.id,
151
+ :peer => e.sender,
152
+ :self => e.receiver,
153
+ :mode => query[:mode],
154
+ :sid => query[:sid]
155
+ )
156
+ end
157
+
158
+ end
159
+
160
+ class Event < Cancer::Events::AbstractEvent
161
+ attr_reader :bytestream, :options
162
+ def initialize(controller, options)
163
+ @controller, @bytestream, @options = controller, nil, options
164
+ end
165
+
166
+ def accept!(&proc)
167
+ if block_given?
168
+ @controller.enqueue(self, proc) do |e, proc|
169
+ bs = e.connect_with_socks5_proxy_and_handle
170
+ proc.call(bs) if bs
171
+ end
172
+ else
173
+ bs = self.connect_with_socks5_proxy_and_handle
174
+ proc.call(bs) if bs
175
+ end
176
+ end
177
+
178
+ def reject!
179
+ @controller.send_iq(@options[:peer], :error, @options[:iq_id]) do |x|
180
+ x.error(:code => 406, :type => 'auth') do
181
+ x.send('not-acceptable', :xmlns => 'urn:ietf:params:xml:ns:xmpp-stanzas')
182
+ end
183
+ end
184
+ end
185
+
186
+ def connect_with_socks5_proxy_and_handle
187
+ @bytestream = connect_with_socks5_proxy
188
+
189
+ if @bytestream
190
+ @controller.send_iq(@options[:peer], :result, @options[:iq_id]) do |x|
191
+ x.query(:xmlns => Cancer::XEP_0065::NS, :sid => @options[:sid]) do
192
+ x.send('streamhost-used', :jid => @bytestream.streamhost)
193
+ end
194
+ end
195
+ else
196
+ @controller.send_iq(@options[:peer], :error, @options[:iq_id]) do |x|
197
+ x.error(:code => 404, :type => 'cancel') do
198
+ x.send('item-not-found', :xmlns => 'urn:ietf:params:xml:ns:xmpp-stanzas')
199
+ end
200
+ end
201
+ return false
202
+ end
203
+
204
+ return @bytestream
205
+ end
206
+
207
+ private
208
+
209
+ def connect_with_socks5_proxy
210
+ @options[:streamhosts].each do |jid, streamhost|
211
+
212
+ begin
213
+
214
+ address_base = "#{@options[:sid]}#{@options[:peer]}#{@options[:self]}"
215
+ address = Digest::SHA1.hexdigest(address_base)
216
+
217
+ socket = TCPSocket.new(streamhost[:host].to_s, streamhost[:port])
218
+ socket.extend Cancer::XEP_0065::TCPSocketHelper
219
+ socket.extend Cancer::XEP_0065::SOCKS_5
220
+ socket.auth
221
+ socket.connect_domain(address, 0)
222
+
223
+ socket.peer = @options[:from].to_s
224
+ socket.stream_id = @options[:sid]
225
+ socket.streamhost = jid.to_s
226
+
227
+ return socket
228
+ rescue IOError => e
229
+ Cancer.logger.debug e
230
+ end
231
+ end
232
+ return nil
233
+ end
234
+
235
+ end
236
+
237
+ class EventMatcher < Cancer::Events::Matcher
238
+ def match?(event)
239
+ Cancer::XEP_0065::Event === event
240
+ end
241
+ end
242
+
243
+ module EventDSL
244
+ def socks5_bytestream
245
+ Cancer::XEP_0065::EventMatcher.new
246
+ end
247
+ end
248
+
249
+ module TCPSocketHelper
250
+ attr_accessor :peer, :stream_id, :streamhost
251
+ end
252
+
253
+ module SOCKS_5
254
+ class Error < IOError ; end
255
+ def auth
256
+ write("\x05\x01\x00")
257
+ buf = read(2)
258
+ if buf.nil? or buf != "\x05\x00"
259
+ close
260
+ raise SOCKS_5::Error.new("Invalid SOCKS5 authentication: #{buf.inspect}")
261
+ end
262
+
263
+ self
264
+ end
265
+
266
+ def connect_domain(domain, port)
267
+ buf_out = "\x05\x01\x00\x03#{domain.size.chr}#{domain}#{[port].pack("n")}"
268
+ write(buf_out)
269
+ buf = read(4)
270
+ if buf.nil? or buf[0..1] != "\005\000"
271
+ close
272
+ raise SOCKS_5::Error.new("Invalid SOCKS5 connect: #{buf_out.inspect} => #{buf.inspect}")
273
+ end
274
+
275
+ case buf.respond_to?(:bytes) ? buf.bytes.to_a[3] : buf[3]
276
+ when 1 then read(6) # IPv4 addr
277
+ when 3 then read(3 + domain.size) # Domain
278
+ when 4 then read(18) # IPv6 addr
279
+ else
280
+ close
281
+ raise SOCKS_5::Error.new("Invalid SOCKS5 address type #{buf[3].to_s}")
282
+ end
283
+
284
+ self
285
+ end
286
+ end
287
+
288
+ class StreamMethod < Cancer::XEP_0095::StreamMethod
289
+ def handle(to, id, &proc)
290
+ @stream.open_socks5_bytestream(to, id, &proc)
291
+ end
292
+ end
293
+
294
+ class SOCKS5Controller < Cancer::Controller
295
+ on { |e| e.socks5_bytestream }
296
+ def catch_socks5(e)
297
+ controller = self.controllers_by_name['Cancer::XEP_0095::Controller']
298
+ si_event = controller.pending_streams[e.options[:sid].to_s]
299
+ if si_event
300
+ si_event.caught_stream!(e)
301
+ end
302
+ end
303
+ end
304
+
305
+ end
306
+ end