em-xmpp 0.0.11 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/xmig +40 -37
- data/em-xmpp.gemspec +1 -0
- data/lib/em-xmpp.rb +0 -2
- data/lib/em-xmpp/component.rb +18 -0
- data/lib/em-xmpp/connection.rb +35 -103
- data/lib/em-xmpp/context.rb +37 -19
- data/lib/em-xmpp/entity.rb +219 -223
- data/lib/em-xmpp/evented.rb +183 -0
- data/lib/em-xmpp/handler.rb +89 -86
- data/lib/em-xmpp/helpers.rb +39 -38
- data/lib/em-xmpp/namespaces.rb +1 -0
- data/lib/em-xmpp/non-em.rb +95 -0
- data/lib/em-xmpp/version.rb +1 -1
- data/lib/em-xmpp/xml_builder.rb +160 -0
- data/lib/em-xmpp/xml_parser.rb +344 -0
- data/samples/hello.rb +6 -3
- data/samples/non-em-hello.rb +90 -0
- metadata +25 -9
- data/lib/em-xmpp/connector.rb +0 -244
@@ -0,0 +1,183 @@
|
|
1
|
+
|
2
|
+
require 'em-xmpp/namespaces'
|
3
|
+
require 'em-xmpp/entity'
|
4
|
+
require 'em-xmpp/handler'
|
5
|
+
require 'em-xmpp/xml_builder'
|
6
|
+
require 'em-xmpp/xml_parser'
|
7
|
+
require 'em-xmpp/cert_store'
|
8
|
+
require 'fiber'
|
9
|
+
|
10
|
+
module EM::Xmpp
|
11
|
+
module Evented
|
12
|
+
include Namespaces
|
13
|
+
include XmlBuilder
|
14
|
+
include XmlParser
|
15
|
+
def component?
|
16
|
+
@component
|
17
|
+
end
|
18
|
+
|
19
|
+
def jid_received(jid)
|
20
|
+
@jid = entity jid
|
21
|
+
end
|
22
|
+
|
23
|
+
def entity(jid)
|
24
|
+
Entity.new(self, jid)
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_presence_params
|
28
|
+
{}
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_message_params
|
32
|
+
{'to' => @jid.domain, 'id' => "em-xmpp.#{rand(65535)}"}
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_iq_params
|
36
|
+
{'type' => 'get', 'id' => "em-xmpp.#{rand(65535)}"}
|
37
|
+
end
|
38
|
+
|
39
|
+
def presence_stanza(*args,&blk)
|
40
|
+
OutgoingStanza.new('presence', default_presence_params, *args,&blk)
|
41
|
+
end
|
42
|
+
|
43
|
+
def message_stanza(*args,&blk)
|
44
|
+
OutgoingStanza.new('message',default_message_params,*args,&blk)
|
45
|
+
end
|
46
|
+
|
47
|
+
def iq_stanza(*args,&blk)
|
48
|
+
OutgoingStanza.new('iq', default_iq_params, *args,&blk)
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_stanza(stanza)
|
52
|
+
send_raw stanza.xml
|
53
|
+
if block_given?
|
54
|
+
upon(:anything) do |ctx|
|
55
|
+
if ctx.bit(:stanza).id == stanza.params['id']
|
56
|
+
yield ctx
|
57
|
+
ctx.delete_xpath_handler!
|
58
|
+
else
|
59
|
+
ctx
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
%w{upon on on_exception on_presence on_iq on_message on_decorator on_iq_decorator on_presence_decorator on_message_decorator}.each do |meth|
|
66
|
+
define_method(meth) do |*args,&blk|
|
67
|
+
@handler.send meth, *args, &blk
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
# XML (stanzas) stream
|
73
|
+
|
74
|
+
def ready
|
75
|
+
end
|
76
|
+
|
77
|
+
def stream_start(node)
|
78
|
+
end
|
79
|
+
|
80
|
+
def stanza_start(node)
|
81
|
+
end
|
82
|
+
|
83
|
+
def stanza_end(node)
|
84
|
+
Fiber.new { @handler.handle(node) }.resume
|
85
|
+
end
|
86
|
+
|
87
|
+
def unhandled_stanza(node)
|
88
|
+
raise RuntimeError, "did not handle node:\n#{node}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def ssl_verify_peer(pem)
|
92
|
+
@certstore.trusted?(pem).tap do |trusted|
|
93
|
+
close_connection unless trusted
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def receive_raw(dat)
|
98
|
+
@xml_parser << dat
|
99
|
+
end
|
100
|
+
|
101
|
+
def prepare_parser!
|
102
|
+
@xml_parser = ForwardingParser.new self
|
103
|
+
@stack = []
|
104
|
+
@stanza = nil
|
105
|
+
@streamdoc = nil
|
106
|
+
|
107
|
+
open_xml_stream
|
108
|
+
end
|
109
|
+
|
110
|
+
def restart_xml_stream
|
111
|
+
#make sure we stop receiving methods from the old parser
|
112
|
+
@xml_parser.document.recipient = nil
|
113
|
+
prepare_parser!
|
114
|
+
end
|
115
|
+
|
116
|
+
def send_xml(*args)
|
117
|
+
send_raw build_xml(*args)
|
118
|
+
end
|
119
|
+
|
120
|
+
def set_negotiation_handler!
|
121
|
+
@handler = StreamNegotiation.new self
|
122
|
+
end
|
123
|
+
|
124
|
+
def negotiation_finished
|
125
|
+
@pass = nil
|
126
|
+
@handler = Routine.new self
|
127
|
+
send_stanza presence_stanza() unless component?
|
128
|
+
framework_ready if respond_to? :framework_ready
|
129
|
+
ready
|
130
|
+
end
|
131
|
+
|
132
|
+
def negotiation_failed(node)
|
133
|
+
raise RuntimeError, "could not negotiate a stream:\n#{node}"
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def open_xml_stream_tag
|
139
|
+
domain = @jid.domain
|
140
|
+
version = '1.0'
|
141
|
+
lang = 'en'
|
142
|
+
start_stream = <<-STREAM
|
143
|
+
<stream:stream
|
144
|
+
to='#{domain}'
|
145
|
+
version='#{version}'
|
146
|
+
xml:lang='#{lang}'
|
147
|
+
xmlns='#{Client}'
|
148
|
+
xmlns:stream='#{Stream}'
|
149
|
+
>
|
150
|
+
STREAM
|
151
|
+
end
|
152
|
+
|
153
|
+
def close_xml_stream_tag
|
154
|
+
'</stream:stream>'
|
155
|
+
end
|
156
|
+
|
157
|
+
def open_xml_stream
|
158
|
+
send_raw open_xml_stream_tag
|
159
|
+
end
|
160
|
+
|
161
|
+
def close_xml_stream
|
162
|
+
send_raw close_xml_stream_tag
|
163
|
+
end
|
164
|
+
|
165
|
+
public
|
166
|
+
|
167
|
+
### TLS World
|
168
|
+
|
169
|
+
def ask_for_tls
|
170
|
+
send_xml('starttls', :xmlns => TLS)
|
171
|
+
end
|
172
|
+
|
173
|
+
def start_using_tls_and_reset_stream
|
174
|
+
initiate_tls
|
175
|
+
restart_xml_stream
|
176
|
+
end
|
177
|
+
|
178
|
+
def initiate_tls
|
179
|
+
raise NotImplementedError
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
data/lib/em-xmpp/handler.rb
CHANGED
@@ -3,6 +3,7 @@ require 'em-xmpp/namespaces'
|
|
3
3
|
require 'em-xmpp/context'
|
4
4
|
require 'em-xmpp/stanza_matcher'
|
5
5
|
require 'em-xmpp/stanza_handler'
|
6
|
+
require 'em-xmpp/xml_builder'
|
6
7
|
require 'base64'
|
7
8
|
require 'sasl/base'
|
8
9
|
require 'sasl'
|
@@ -10,14 +11,13 @@ require 'sasl'
|
|
10
11
|
module EM::Xmpp
|
11
12
|
class Handler
|
12
13
|
include Namespaces
|
14
|
+
include XmlBuilder
|
13
15
|
|
14
16
|
def initialize(conn)
|
15
17
|
@connection = conn
|
16
18
|
@handlers = []
|
17
19
|
@decorator_handlers = []
|
18
20
|
@exception_handlers = []
|
19
|
-
|
20
|
-
stack_decorators
|
21
21
|
end
|
22
22
|
|
23
23
|
# wraps the stanza in a context and calls handle_context
|
@@ -25,82 +25,82 @@ module EM::Xmpp
|
|
25
25
|
handle_context Context.new(@connection, stanza)
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
def stack_decorators
|
28
|
+
def enable_default_stack_decorators!
|
31
29
|
on_presence_decorator do |ctx|
|
32
|
-
presence = ctx.bit
|
33
|
-
ctx.bit
|
30
|
+
presence = ctx.bit(:presence)
|
31
|
+
ctx.bit(:error) if presence.error?
|
34
32
|
ctx
|
35
33
|
end
|
36
34
|
on_message_decorator do |ctx|
|
37
|
-
msg = ctx.bit
|
38
|
-
ctx.bit
|
35
|
+
msg = ctx.bit(:message)
|
36
|
+
ctx.bit(:error) if msg.error?
|
39
37
|
ctx
|
40
38
|
end
|
41
39
|
on_iq_decorator do |ctx|
|
42
|
-
iq = ctx.bit
|
43
|
-
ctx.bit
|
40
|
+
iq = ctx.bit(:iq)
|
41
|
+
ctx.bit(:error) if iq.error?
|
44
42
|
ctx
|
45
43
|
end
|
46
44
|
on_decorator('//xmlns:pubsub', 'xmlns' => PubSub) do |ctx|
|
47
|
-
ctx.bit
|
45
|
+
ctx.bit(:pubsub)
|
48
46
|
ctx
|
49
47
|
end
|
50
48
|
on_decorator('//xmlns:event', 'xmlns' => PubSubEvent) do |ctx|
|
51
|
-
ctx.bit
|
49
|
+
ctx.bit(:pubsubevent)
|
52
50
|
ctx
|
53
51
|
end
|
54
52
|
on_decorator('//xmlns:delay', 'xmlns' => Delay) do |ctx|
|
55
|
-
ctx.bit
|
53
|
+
ctx.bit(:delay)
|
56
54
|
ctx
|
57
55
|
end
|
58
56
|
on_decorator('//xmlns:query', 'xmlns' => DiscoverInfos) do |ctx|
|
59
|
-
ctx.bit
|
57
|
+
ctx.bit(:discoinfos)
|
60
58
|
ctx
|
61
59
|
end
|
62
60
|
on_decorator('//xmlns:query', 'xmlns' => DiscoverItems) do |ctx|
|
63
|
-
ctx.bit
|
61
|
+
ctx.bit(:discoitems)
|
64
62
|
ctx
|
65
63
|
end
|
66
64
|
on_decorator('//xmlns:query', 'xmlns' => Roster) do |ctx|
|
67
|
-
ctx.bit
|
65
|
+
ctx.bit(:roster)
|
68
66
|
ctx
|
69
67
|
end
|
70
68
|
on_decorator('//xmlns:command', 'xmlns' => Commands) do |ctx|
|
71
|
-
ctx.bit
|
69
|
+
ctx.bit(:command)
|
72
70
|
ctx
|
73
71
|
end
|
74
72
|
on_decorator('//xmlns:data', 'xmlns' => BoB) do |ctx|
|
75
|
-
ctx.bit
|
73
|
+
ctx.bit(:bob)
|
76
74
|
ctx
|
77
75
|
end
|
78
76
|
on_decorator('//xmlns:x', 'xmlns' => DataForms) do |ctx|
|
79
|
-
ctx.bit
|
77
|
+
ctx.bit(:dataforms)
|
80
78
|
ctx
|
81
79
|
end
|
82
80
|
on_decorator('//xmlns:nick', 'xmlns' => Nick) do |ctx|
|
83
|
-
ctx.bit
|
81
|
+
ctx.bit(:nickname)
|
84
82
|
ctx
|
85
83
|
end
|
86
84
|
on_decorator('//xmlns:x', 'xmlns' => MucUser) do |ctx|
|
87
|
-
ctx.bit
|
85
|
+
ctx.bit(:mucuser)
|
88
86
|
ctx
|
89
87
|
end
|
90
88
|
on_decorator('//xmlns:si', 'xmlns' => StreamInitiation) do |ctx|
|
91
|
-
ctx.bit
|
89
|
+
ctx.bit(:streaminitiation)
|
92
90
|
ctx
|
93
91
|
end
|
94
92
|
on_decorator('//xmlns:open | //xmlns:data | //xmlns:close', 'xmlns' => IBB) do |ctx|
|
95
|
-
ctx.bit
|
93
|
+
ctx.bit(:ibb)
|
96
94
|
ctx
|
97
95
|
end
|
98
96
|
on_decorator('//xmlns:query', 'xmlns' => ByteStreams) do |ctx|
|
99
|
-
ctx.bit
|
97
|
+
ctx.bit(:bytestreams)
|
100
98
|
ctx
|
101
99
|
end
|
102
100
|
end
|
103
101
|
|
102
|
+
private
|
103
|
+
|
104
104
|
def add_decorator_handler(handler)
|
105
105
|
@decorator_handlers << handler
|
106
106
|
end
|
@@ -273,63 +273,73 @@ module EM::Xmpp
|
|
273
273
|
on_exception(:anything) do |ctx|
|
274
274
|
raise ctx['error']
|
275
275
|
end
|
276
|
-
on('//xmlns:starttls', {'xmlns' => TLS}) do |ctx|
|
277
|
-
@connection.ask_for_tls
|
278
|
-
ctx.delete_xpath_handler!.done!
|
279
|
-
end
|
280
276
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
277
|
+
if @connection.component?
|
278
|
+
on('//xmlns:handshake', {}) do |ctx|
|
279
|
+
@connection.negotiation_finished
|
280
|
+
ctx.delete_xpath_handler!.done!
|
281
|
+
end
|
285
282
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
mechanisms = search.first.children.map(&:content)
|
290
|
-
start_sasl mechanisms
|
283
|
+
else
|
284
|
+
on('//xmlns:starttls', {'xmlns' => TLS}) do |ctx|
|
285
|
+
@connection.ask_for_tls
|
291
286
|
ctx.delete_xpath_handler!.done!
|
292
|
-
else
|
293
|
-
raise RuntimeError, "how come there is no mechanism node?"
|
294
287
|
end
|
295
|
-
end
|
296
288
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
289
|
+
on('//xmlns:proceed', {'xmlns' => TLS }) do |ctx|
|
290
|
+
@connection.start_using_tls_and_reset_stream
|
291
|
+
ctx.delete_xpath_handler!.done!
|
292
|
+
end
|
301
293
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
294
|
+
on('//xmlns:mechanisms', {'xmlns' => SASL}) do |ctx|
|
295
|
+
search = ctx.xpath('//xmlns:mechanisms', {'xmlns' => SASL})
|
296
|
+
if search.first
|
297
|
+
mechanisms = search.first.children.map(&:content)
|
298
|
+
start_sasl mechanisms
|
299
|
+
ctx.delete_xpath_handler!.done!
|
300
|
+
else
|
301
|
+
raise RuntimeError, "how come there is no mechanism node?"
|
302
|
+
end
|
303
|
+
end
|
306
304
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
305
|
+
on('//xmlns:challenge', {'xmlns' => SASL}) do |ctx|
|
306
|
+
sasl_step ctx.stanza
|
307
|
+
ctx.done!
|
308
|
+
end
|
311
309
|
|
312
|
-
|
313
|
-
|
310
|
+
on('//xmlns:success', {'xmlns' => SASL}) do |ctx|
|
311
|
+
@connection.restart_xml_stream
|
312
|
+
ctx.delete_xpath_handler!.done!
|
313
|
+
end
|
314
314
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
else
|
319
|
-
raise RuntimeError, "no jid despite binding"
|
315
|
+
on('//xmlns:bind', {'xmlns' => Bind}) do |ctx|
|
316
|
+
bind_to_resource
|
317
|
+
ctx.delete_xpath_handler!.done!
|
320
318
|
end
|
321
319
|
|
322
|
-
ctx
|
323
|
-
|
320
|
+
on('//xmlns:bind', {'xmlns' => Bind}) do |ctx|
|
321
|
+
jid = extract_jid ctx.stanza
|
324
322
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
323
|
+
if jid
|
324
|
+
@connection.jid_received jid
|
325
|
+
start_session
|
326
|
+
else
|
327
|
+
raise RuntimeError, "no jid despite binding"
|
328
|
+
end
|
329
|
+
|
330
|
+
ctx.delete_xpath_handler!.done!
|
331
|
+
end
|
332
|
+
|
333
|
+
on('//xmlns:session', {'xmlns' => Session}) do |ctx|
|
334
|
+
@connection.negotiation_finished
|
335
|
+
ctx.delete_xpath_handler!.done!
|
336
|
+
end
|
337
|
+
|
338
|
+
on('//xmlns:failure', {'xmlns' => SASL}) do |ctx|
|
339
|
+
@connection.negotiation_failed(ctx.stanza)
|
340
|
+
ctx.done!
|
341
|
+
end
|
329
342
|
|
330
|
-
on('//xmlns:failure', {'xmlns' => SASL}) do |ctx|
|
331
|
-
@connection.negotiation_failed(ctx.stanza)
|
332
|
-
ctx.done!
|
333
343
|
end
|
334
344
|
|
335
345
|
on(:anything) do |ctx|
|
@@ -341,24 +351,23 @@ module EM::Xmpp
|
|
341
351
|
|
342
352
|
def extract_jid(stanza)
|
343
353
|
jid = stanza.xpath('//bind:jid', {'bind' => Bind})
|
344
|
-
jid.text if jid.any?
|
354
|
+
jid.first.text if jid.any?
|
345
355
|
end
|
346
356
|
|
347
357
|
def bind_to_resource(wanted_res=nil)
|
348
|
-
c.send_stanza(c.iq_stanza('type' => 'set'
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
358
|
+
c.send_stanza(c.iq_stanza({'type' => 'set'},
|
359
|
+
x('bind',{'xmlns' => Bind},
|
360
|
+
x_if(wanted_res,'resource',wanted_res)
|
361
|
+
)
|
362
|
+
)
|
363
|
+
)
|
353
364
|
end
|
354
365
|
|
355
366
|
def start_session
|
356
|
-
session_request = c.iq_stanza('type' => 'set', 'to' => jid.domain
|
357
|
-
x.session('xmlns' => Session)
|
358
|
-
end
|
367
|
+
session_request = c.iq_stanza({'type' => 'set', 'to' => jid.domain}, x('session','xmlns' => Session))
|
359
368
|
|
360
369
|
c.send_stanza(session_request) do |ctx|
|
361
|
-
if ctx.bit
|
370
|
+
if ctx.bit(:stanza).type == 'result'
|
362
371
|
@connection.negotiation_finished
|
363
372
|
ctx.delete_xpath_handler!.done!
|
364
373
|
else
|
@@ -385,13 +394,7 @@ module EM::Xmpp
|
|
385
394
|
end
|
386
395
|
|
387
396
|
def reply_sasl(msg, val=nil, mech=nil)
|
388
|
-
c.send_xml
|
389
|
-
if val
|
390
|
-
x.send(msg, val, {'xmlns' => SASL, 'mechanism' => mech})
|
391
|
-
else
|
392
|
-
x.send(msg, {'xmlns' => SASL, 'mechanism' => mech})
|
393
|
-
end
|
394
|
-
end
|
397
|
+
c.send_xml(msg, val, 'xmlns' => SASL, 'mechanism' => mech)
|
395
398
|
end
|
396
399
|
|
397
400
|
end
|