tp-blather 0.8.2
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/.autotest +13 -0
- data/.gemtest +0 -0
- data/.gitignore +19 -0
- data/.rspec +3 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +249 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/LICENSE +22 -0
- data/README.md +413 -0
- data/Rakefile +20 -0
- data/TODO.md +2 -0
- data/blather.gemspec +51 -0
- data/examples/certs/README +20 -0
- data/examples/certs/ca-bundle.crt +3987 -0
- data/examples/echo.rb +19 -0
- data/examples/execute.rb +17 -0
- data/examples/ping_pong.rb +38 -0
- data/examples/print_hierarchy.rb +77 -0
- data/examples/rosterprint.rb +15 -0
- data/examples/stream_only.rb +28 -0
- data/examples/trusted_echo.rb +21 -0
- data/examples/xmpp4r/echo.rb +36 -0
- data/lib/blather.rb +112 -0
- data/lib/blather/cert_store.rb +53 -0
- data/lib/blather/client.rb +95 -0
- data/lib/blather/client/client.rb +345 -0
- data/lib/blather/client/dsl.rb +320 -0
- data/lib/blather/client/dsl/pubsub.rb +174 -0
- data/lib/blather/core_ext/eventmachine.rb +125 -0
- data/lib/blather/core_ext/ipaddr.rb +20 -0
- data/lib/blather/errors.rb +69 -0
- data/lib/blather/errors/sasl_error.rb +44 -0
- data/lib/blather/errors/stanza_error.rb +110 -0
- data/lib/blather/errors/stream_error.rb +84 -0
- data/lib/blather/file_transfer.rb +107 -0
- data/lib/blather/file_transfer/ibb.rb +68 -0
- data/lib/blather/file_transfer/s5b.rb +114 -0
- data/lib/blather/jid.rb +141 -0
- data/lib/blather/roster.rb +118 -0
- data/lib/blather/roster_item.rb +146 -0
- data/lib/blather/stanza.rb +167 -0
- data/lib/blather/stanza/disco.rb +32 -0
- data/lib/blather/stanza/disco/capabilities.rb +161 -0
- data/lib/blather/stanza/disco/disco_info.rb +205 -0
- data/lib/blather/stanza/disco/disco_items.rb +134 -0
- data/lib/blather/stanza/iq.rb +144 -0
- data/lib/blather/stanza/iq/command.rb +339 -0
- data/lib/blather/stanza/iq/ibb.rb +86 -0
- data/lib/blather/stanza/iq/ping.rb +50 -0
- data/lib/blather/stanza/iq/query.rb +53 -0
- data/lib/blather/stanza/iq/roster.rb +185 -0
- data/lib/blather/stanza/iq/s5b.rb +208 -0
- data/lib/blather/stanza/iq/si.rb +415 -0
- data/lib/blather/stanza/iq/vcard.rb +149 -0
- data/lib/blather/stanza/message.rb +428 -0
- data/lib/blather/stanza/message/muc_user.rb +119 -0
- data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
- data/lib/blather/stanza/presence.rb +172 -0
- data/lib/blather/stanza/presence/c.rb +100 -0
- data/lib/blather/stanza/presence/muc.rb +35 -0
- data/lib/blather/stanza/presence/muc_user.rb +147 -0
- data/lib/blather/stanza/presence/status.rb +218 -0
- data/lib/blather/stanza/presence/subscription.rb +100 -0
- data/lib/blather/stanza/pubsub.rb +119 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
- data/lib/blather/stanza/pubsub/create.rb +65 -0
- data/lib/blather/stanza/pubsub/errors.rb +18 -0
- data/lib/blather/stanza/pubsub/event.rb +139 -0
- data/lib/blather/stanza/pubsub/items.rb +103 -0
- data/lib/blather/stanza/pubsub/publish.rb +103 -0
- data/lib/blather/stanza/pubsub/retract.rb +92 -0
- data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
- data/lib/blather/stanza/pubsub/subscription.rb +135 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
- data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
- data/lib/blather/stanza/pubsub_owner.rb +51 -0
- data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
- data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
- data/lib/blather/stanza/x.rb +416 -0
- data/lib/blather/stream.rb +266 -0
- data/lib/blather/stream/client.rb +32 -0
- data/lib/blather/stream/component.rb +39 -0
- data/lib/blather/stream/features.rb +70 -0
- data/lib/blather/stream/features/register.rb +38 -0
- data/lib/blather/stream/features/resource.rb +63 -0
- data/lib/blather/stream/features/sasl.rb +190 -0
- data/lib/blather/stream/features/session.rb +45 -0
- data/lib/blather/stream/features/tls.rb +29 -0
- data/lib/blather/stream/parser.rb +102 -0
- data/lib/blather/version.rb +3 -0
- data/lib/blather/xmpp_node.rb +94 -0
- data/spec/blather/client/client_spec.rb +687 -0
- data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
- data/spec/blather/client/dsl_spec.rb +266 -0
- data/spec/blather/errors/sasl_error_spec.rb +33 -0
- data/spec/blather/errors/stanza_error_spec.rb +129 -0
- data/spec/blather/errors/stream_error_spec.rb +108 -0
- data/spec/blather/errors_spec.rb +33 -0
- data/spec/blather/file_transfer_spec.rb +135 -0
- data/spec/blather/jid_spec.rb +87 -0
- data/spec/blather/roster_item_spec.rb +134 -0
- data/spec/blather/roster_spec.rb +107 -0
- data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
- data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
- data/spec/blather/stanza/iq/command_spec.rb +206 -0
- data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
- data/spec/blather/stanza/iq/ping_spec.rb +45 -0
- data/spec/blather/stanza/iq/query_spec.rb +64 -0
- data/spec/blather/stanza/iq/roster_spec.rb +139 -0
- data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
- data/spec/blather/stanza/iq/si_spec.rb +98 -0
- data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
- data/spec/blather/stanza/iq_spec.rb +61 -0
- data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
- data/spec/blather/stanza/message_spec.rb +282 -0
- data/spec/blather/stanza/presence/c_spec.rb +56 -0
- data/spec/blather/stanza/presence/muc_spec.rb +37 -0
- data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
- data/spec/blather/stanza/presence/status_spec.rb +144 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
- data/spec/blather/stanza/presence_spec.rb +125 -0
- data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
- data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
- data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
- data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
- data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
- data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
- data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
- data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
- data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
- data/spec/blather/stanza/pubsub_spec.rb +68 -0
- data/spec/blather/stanza/x_spec.rb +231 -0
- data/spec/blather/stanza_spec.rb +134 -0
- data/spec/blather/stream/client_spec.rb +1090 -0
- data/spec/blather/stream/component_spec.rb +108 -0
- data/spec/blather/stream/parser_spec.rb +152 -0
- data/spec/blather/stream/ssl_spec.rb +32 -0
- data/spec/blather/xmpp_node_spec.rb +47 -0
- data/spec/blather_spec.rb +34 -0
- data/spec/fixtures/pubsub.rb +311 -0
- data/spec/spec_helper.rb +17 -0
- data/yard/templates/default/class/html/handlers.erb +18 -0
- data/yard/templates/default/class/setup.rb +10 -0
- data/yard/templates/default/class/text/handlers.erb +1 -0
- metadata +459 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
module Blather
|
|
2
|
+
|
|
3
|
+
# # A pure XMPP stream.
|
|
4
|
+
#
|
|
5
|
+
# Blather::Stream can be used to build your own handler system if Blather's
|
|
6
|
+
# doesn't suit your needs. It will take care of the entire connection
|
|
7
|
+
# process then start sending Stanza objects back to the registered client.
|
|
8
|
+
#
|
|
9
|
+
# The client you register with Blather::Stream needs to implement the following
|
|
10
|
+
# methods:
|
|
11
|
+
# * #post_init(stream, jid = nil)
|
|
12
|
+
# Called after the stream has been initiated.
|
|
13
|
+
# @param [Blather::Stream] stream is the connected stream object
|
|
14
|
+
# @param [Blather::JID, nil] jid is the full JID as recognized by the server
|
|
15
|
+
#
|
|
16
|
+
# * #receive_data(stanza)
|
|
17
|
+
# Called every time the stream receives a new stanza
|
|
18
|
+
# @param [Blather::Stanza] stanza a stanza object from the server
|
|
19
|
+
#
|
|
20
|
+
# * #unbind
|
|
21
|
+
# Called when the stream is shutdown. This will be called regardless of which
|
|
22
|
+
# side shut the stream down.
|
|
23
|
+
#
|
|
24
|
+
# @example Create a new stream and handle it with our own class
|
|
25
|
+
# class MyClient
|
|
26
|
+
# attr :jid
|
|
27
|
+
#
|
|
28
|
+
# def post_init(stream, jid = nil)
|
|
29
|
+
# @stream = stream
|
|
30
|
+
# self.jid = jid
|
|
31
|
+
# p "Stream Started"
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# # Pretty print the stream
|
|
35
|
+
# def receive_data(stanza)
|
|
36
|
+
# pp stanza
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# def unbind
|
|
40
|
+
# p "Stream Ended"
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# def write(what)
|
|
44
|
+
# @stream.write what
|
|
45
|
+
# end
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# client = Blather::Stream.start MyClient.new, "jid@domain/res", "pass"
|
|
49
|
+
# client.write "[pure xml over the wire]"
|
|
50
|
+
class Stream < EventMachine::Connection
|
|
51
|
+
# Connection not found
|
|
52
|
+
class NoConnection < RuntimeError; end
|
|
53
|
+
class ConnectionFailed < RuntimeError; end
|
|
54
|
+
class ConnectionTimeout < RuntimeError; end
|
|
55
|
+
|
|
56
|
+
# @private
|
|
57
|
+
STREAM_NS = 'http://etherx.jabber.org/streams'
|
|
58
|
+
attr_accessor :password
|
|
59
|
+
attr_reader :jid
|
|
60
|
+
@@store = nil
|
|
61
|
+
|
|
62
|
+
# Start the stream between client and server
|
|
63
|
+
#
|
|
64
|
+
# @param [Object] client an object that will respond to #post_init,
|
|
65
|
+
# #unbind #receive_data
|
|
66
|
+
# @param [Blather::JID, #to_s] jid the jid to authenticate with
|
|
67
|
+
# @param [String] pass the password to authenticate with
|
|
68
|
+
# @param [String, nil] host the hostname or IP to connect to. Default is
|
|
69
|
+
# to use the domain on the JID
|
|
70
|
+
# @param [Fixnum, nil] port the port to connect on. Default is the XMPP
|
|
71
|
+
# default of 5222
|
|
72
|
+
# @param [String, nil] certs the trusted cert store in pem format to verify
|
|
73
|
+
# communication with the server is trusted.
|
|
74
|
+
# @param [Fixnum, nil] connect_timeout the number of seconds for which to wait for a successful connection
|
|
75
|
+
def self.start(client, jid, pass, host = nil, port = nil, certs_directory = nil, connect_timeout = nil)
|
|
76
|
+
jid = JID.new jid
|
|
77
|
+
port ||= 5222
|
|
78
|
+
if certs_directory
|
|
79
|
+
@@store = CertStore.new(certs_directory)
|
|
80
|
+
end
|
|
81
|
+
if host
|
|
82
|
+
connect host, port, self, client, jid, pass, connect_timeout
|
|
83
|
+
else
|
|
84
|
+
require 'resolv'
|
|
85
|
+
srv = []
|
|
86
|
+
Resolv::DNS.open do |dns|
|
|
87
|
+
srv = dns.getresources(
|
|
88
|
+
"_xmpp-client._tcp.#{jid.domain}",
|
|
89
|
+
Resolv::DNS::Resource::IN::SRV
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if srv.empty?
|
|
94
|
+
connect jid.domain, port, self, client, jid, pass, connect_timeout
|
|
95
|
+
else
|
|
96
|
+
srv.sort! do |a,b|
|
|
97
|
+
(a.priority != b.priority) ? (a.priority <=> b.priority) :
|
|
98
|
+
(b.weight <=> a.weight)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
srv.detect do |r|
|
|
102
|
+
not connect(r.target.to_s, r.port, self, client, jid, pass, connect_timeout) === false
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Attempt a connection
|
|
109
|
+
# Stream will raise +NoConnection+ if it receives #unbind before #post_init
|
|
110
|
+
# this catches that and returns false prompting for another attempt
|
|
111
|
+
# @private
|
|
112
|
+
def self.connect(host, port, conn, client, jid, pass, connect_timeout = nil)
|
|
113
|
+
EM.connect host, port, conn, client, jid, pass, connect_timeout
|
|
114
|
+
rescue NoConnection
|
|
115
|
+
false
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
[:started, :stopped, :ready, :negotiating].each do |state|
|
|
119
|
+
define_method("#{state}?") { @state == state }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Send data over the wire
|
|
123
|
+
#
|
|
124
|
+
# @todo Queue if not ready
|
|
125
|
+
#
|
|
126
|
+
# @param [#to_xml, #to_s] stanza the stanza to send over the wire
|
|
127
|
+
def send(stanza)
|
|
128
|
+
data = stanza.respond_to?(:to_xml) ? stanza.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML) : stanza.to_s
|
|
129
|
+
Blather.log "SENDING: (#{caller[1]}) #{stanza}"
|
|
130
|
+
EM.next_tick { send_data data }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Called by EM.connect to initialize stream variables
|
|
134
|
+
# @private
|
|
135
|
+
def initialize(client, jid, pass, connect_timeout = nil)
|
|
136
|
+
super()
|
|
137
|
+
|
|
138
|
+
@error = nil
|
|
139
|
+
@receiver = @client = client
|
|
140
|
+
|
|
141
|
+
self.jid = jid
|
|
142
|
+
@to = self.jid.domain
|
|
143
|
+
@password = pass
|
|
144
|
+
@connect_timeout = connect_timeout || 180
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Called when EM completes the connection to the server
|
|
148
|
+
# this kicks off the starttls/authorize/bind process
|
|
149
|
+
# @private
|
|
150
|
+
def connection_completed
|
|
151
|
+
if @connect_timeout
|
|
152
|
+
@connect_timer = EM::Timer.new @connect_timeout do
|
|
153
|
+
raise ConnectionTimeout, "Stream timed out after #{@connect_timeout} seconds." unless started?
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
@connected = true
|
|
157
|
+
# @keepalive = EM::PeriodicTimer.new(60) { send_data ' ' }
|
|
158
|
+
start
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Called by EM with data from the wire
|
|
162
|
+
# @private
|
|
163
|
+
def receive_data(data)
|
|
164
|
+
@parser << data
|
|
165
|
+
|
|
166
|
+
rescue ParseError => e
|
|
167
|
+
@error = e
|
|
168
|
+
send "<stream:error><xml-not-well-formed xmlns='#{StreamError::STREAM_ERR_NS}'/></stream:error>"
|
|
169
|
+
stop
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Called by EM to verify the peer certificate. If a certificate store directory
|
|
173
|
+
# has not been configured don't worry about peer verification. At least it is encrypted
|
|
174
|
+
# We Log the certificate so that you can add it to the trusted store easily if desired
|
|
175
|
+
# @private
|
|
176
|
+
def ssl_verify_peer(pem)
|
|
177
|
+
# EM is supposed to close the connection when this returns false,
|
|
178
|
+
# but it only does that for inbound connections, not when we
|
|
179
|
+
# make a connection to another server.
|
|
180
|
+
Blather.log "Checking SSL cert: #{pem}"
|
|
181
|
+
return true if !@@store
|
|
182
|
+
@@store.trusted?(pem).tap do |trusted|
|
|
183
|
+
close_connection unless trusted
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Called by EM after the connection has started
|
|
188
|
+
# @private
|
|
189
|
+
def post_init
|
|
190
|
+
@inited = true
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Called by EM when the connection is closed
|
|
194
|
+
# @private
|
|
195
|
+
def unbind
|
|
196
|
+
raise NoConnection unless @inited
|
|
197
|
+
raise ConnectionFailed unless @connected
|
|
198
|
+
|
|
199
|
+
@connect_timer.cancel if @connect_timer
|
|
200
|
+
# @keepalive.cancel
|
|
201
|
+
@state = :stopped
|
|
202
|
+
@client.receive_data @error if @error
|
|
203
|
+
@client.unbind
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Called by the parser with parsed nodes
|
|
207
|
+
# @private
|
|
208
|
+
def receive(node)
|
|
209
|
+
Blather.log "RECEIVING (#{node.element_name}) #{node}"
|
|
210
|
+
@node = node
|
|
211
|
+
|
|
212
|
+
if @node.namespace && @node.namespace.prefix == 'stream'
|
|
213
|
+
case @node.element_name
|
|
214
|
+
when 'stream'
|
|
215
|
+
@state = :ready if @state == :stopped
|
|
216
|
+
return
|
|
217
|
+
when 'error'
|
|
218
|
+
handle_stream_error
|
|
219
|
+
return
|
|
220
|
+
when 'end'
|
|
221
|
+
stop
|
|
222
|
+
return
|
|
223
|
+
when 'features'
|
|
224
|
+
@state = :negotiating
|
|
225
|
+
@receiver = Features.new(
|
|
226
|
+
self,
|
|
227
|
+
proc { ready! },
|
|
228
|
+
proc { |err| @error = err; stop }
|
|
229
|
+
)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
@receiver.receive_data @node.to_stanza
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Ensure the JID gets attached to the client
|
|
236
|
+
# @private
|
|
237
|
+
def jid=(new_jid)
|
|
238
|
+
Blather.log "USING JID: #{new_jid}"
|
|
239
|
+
@jid = JID.new new_jid
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
protected
|
|
243
|
+
# Stop the stream
|
|
244
|
+
# @private
|
|
245
|
+
def stop
|
|
246
|
+
unless @state == :stopped
|
|
247
|
+
@state = :stopped
|
|
248
|
+
send '</stream:stream>'
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# @private
|
|
253
|
+
def handle_stream_error
|
|
254
|
+
@error = StreamError.import(@node)
|
|
255
|
+
stop
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# @private
|
|
259
|
+
def ready!
|
|
260
|
+
@state = :started
|
|
261
|
+
@receiver = @client
|
|
262
|
+
@client.post_init self, @jid
|
|
263
|
+
end
|
|
264
|
+
end # Stream
|
|
265
|
+
|
|
266
|
+
end # Blather
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Blather
|
|
2
|
+
class Stream
|
|
3
|
+
|
|
4
|
+
# @private
|
|
5
|
+
class Client < Stream
|
|
6
|
+
LANG = 'en'
|
|
7
|
+
VERSION = '1.0'
|
|
8
|
+
NAMESPACE = 'jabber:client'
|
|
9
|
+
|
|
10
|
+
def start
|
|
11
|
+
@parser = Parser.new self
|
|
12
|
+
start_stream = <<-STREAM
|
|
13
|
+
<stream:stream
|
|
14
|
+
to='#{@to}'
|
|
15
|
+
xmlns='#{NAMESPACE}'
|
|
16
|
+
xmlns:stream='#{STREAM_NS}'
|
|
17
|
+
version='#{VERSION}'
|
|
18
|
+
xml:lang='#{LANG}'
|
|
19
|
+
>
|
|
20
|
+
STREAM
|
|
21
|
+
send start_stream.gsub(/\s+/, ' ')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def send(stanza)
|
|
25
|
+
stanza.from = self.jid if stanza.is_a?(Stanza) && !stanza.from.nil?
|
|
26
|
+
super stanza
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end #Client
|
|
30
|
+
|
|
31
|
+
end #Stream
|
|
32
|
+
end #Blather
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Blather
|
|
2
|
+
class Stream
|
|
3
|
+
|
|
4
|
+
# @private
|
|
5
|
+
class Component < Stream
|
|
6
|
+
NAMESPACE = 'jabber:component:accept'
|
|
7
|
+
|
|
8
|
+
def receive(node) # :nodoc:
|
|
9
|
+
#if node.element_name == 'handshake'
|
|
10
|
+
# ready!
|
|
11
|
+
#else
|
|
12
|
+
super
|
|
13
|
+
#end
|
|
14
|
+
#
|
|
15
|
+
#if node.document.find_first('/stream:stream[not(stream:error)]', :xmlns => NAMESPACE, :stream => STREAM_NS)
|
|
16
|
+
# send("<handshake>#{Digest::SHA1.hexdigest(@node['id']+@password)}</handshake>")
|
|
17
|
+
#end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def send(stanza)
|
|
21
|
+
stanza.from ||= self.jid if stanza.respond_to?(:from) && stanza.respond_to?(:from=)
|
|
22
|
+
super stanza
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def start
|
|
26
|
+
@parser = Parser.new self
|
|
27
|
+
start_stream = <<-STREAM
|
|
28
|
+
<stream:stream
|
|
29
|
+
to='#{@jid}'
|
|
30
|
+
xmlns='#{NAMESPACE}'
|
|
31
|
+
xmlns:stream='#{STREAM_NS}'
|
|
32
|
+
>
|
|
33
|
+
STREAM
|
|
34
|
+
send start_stream.gsub(/\s+/, ' ')
|
|
35
|
+
end
|
|
36
|
+
end #Client
|
|
37
|
+
|
|
38
|
+
end #Stream
|
|
39
|
+
end #Blather
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Blather
|
|
2
|
+
class Stream
|
|
3
|
+
|
|
4
|
+
# @private
|
|
5
|
+
class Features
|
|
6
|
+
@@features = {}
|
|
7
|
+
def self.register(ns)
|
|
8
|
+
@@features[ns] = self
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.from_namespace(ns)
|
|
12
|
+
@@features[ns]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(stream, succeed, fail)
|
|
16
|
+
@stream = stream
|
|
17
|
+
@succeed = succeed
|
|
18
|
+
@fail = fail
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def receive_data(stanza)
|
|
22
|
+
if @feature
|
|
23
|
+
@feature.receive_data stanza
|
|
24
|
+
else
|
|
25
|
+
@features ||= stanza
|
|
26
|
+
next!
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def next!
|
|
31
|
+
@idx = @idx ? @idx+1 : 0
|
|
32
|
+
if stanza = @features.children[@idx]
|
|
33
|
+
if stanza.namespaces['xmlns'] && (klass = self.class.from_namespace(stanza.namespaces['xmlns']))
|
|
34
|
+
@feature = klass.new(
|
|
35
|
+
@stream,
|
|
36
|
+
proc {
|
|
37
|
+
if (klass == Blather::Stream::Register && stanza = feature?(:mechanisms))
|
|
38
|
+
@idx = @features.children.index(stanza)
|
|
39
|
+
@feature = Blather::Stream::SASL.new @stream, proc { next! }, @fail
|
|
40
|
+
@feature.receive_data stanza
|
|
41
|
+
else
|
|
42
|
+
next!
|
|
43
|
+
end
|
|
44
|
+
},
|
|
45
|
+
(klass == Blather::Stream::SASL && feature?(:register)) ? proc { next! } : @fail
|
|
46
|
+
)
|
|
47
|
+
@feature.receive_data stanza
|
|
48
|
+
else
|
|
49
|
+
next!
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
succeed!
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def succeed!
|
|
57
|
+
@succeed.call
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def fail!(msg)
|
|
61
|
+
@fail.call msg
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def feature?(feature)
|
|
65
|
+
@features && @features.children.find { |v| v.element_name == feature.to_s }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end #Stream
|
|
70
|
+
end #Blather
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Blather
|
|
2
|
+
class Stream
|
|
3
|
+
class Register < Features
|
|
4
|
+
REGISTER_NS = "http://jabber.org/features/iq-register".freeze
|
|
5
|
+
|
|
6
|
+
register REGISTER_NS
|
|
7
|
+
|
|
8
|
+
def initialize(stream, succeed, fail)
|
|
9
|
+
super
|
|
10
|
+
@jid = @stream.jid
|
|
11
|
+
@pass = @stream.password
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def receive_data(stanza)
|
|
15
|
+
error_node = stanza.xpath("//error").first
|
|
16
|
+
|
|
17
|
+
if error_node
|
|
18
|
+
fail!(BlatherError.new(stanza))
|
|
19
|
+
elsif stanza['type'] == 'result' && (stanza.content.empty? || stanza.children.find { |v| v.element_name == "query" })
|
|
20
|
+
succeed!
|
|
21
|
+
else
|
|
22
|
+
@stream.send register_query
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def register_query
|
|
27
|
+
node = Blather::Stanza::Iq::Query.new(:set)
|
|
28
|
+
query_node = node.xpath('//query').first
|
|
29
|
+
query_node['xmlns'] = 'jabber:iq:register'
|
|
30
|
+
Nokogiri::XML::Builder.with(query_node) do |xml|
|
|
31
|
+
xml.username @jid.node
|
|
32
|
+
xml.password @pass
|
|
33
|
+
end
|
|
34
|
+
node
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|