em-xmpp 0.0.11 → 0.0.12
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.
- 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
|