tp-blather 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,345 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. .. blather])
|
2
|
+
|
3
|
+
module Blather
|
4
|
+
# # Blather Client
|
5
|
+
#
|
6
|
+
# Blather's Client class provides a set of helpers for working with common
|
7
|
+
# XMPP tasks such as setting up and starting the connection, settings
|
8
|
+
# status, registering and dispatching filters and handlers and roster
|
9
|
+
# management.
|
10
|
+
#
|
11
|
+
# Client can be used separately from the DSL if you'd like to implement your
|
12
|
+
# own DSL Here's the echo example using the client without the DSL:
|
13
|
+
#
|
14
|
+
# require 'blather/client/client'
|
15
|
+
# client = Client.setup 'echo@jabber.local', 'echo'
|
16
|
+
#
|
17
|
+
# client.register_handler(:ready) do
|
18
|
+
# puts "Connected ! send messages to #{client.jid.stripped}."
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# client.register_handler :subscription, :request? do |s|
|
22
|
+
# client.write s.approve!
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# client.register_handler :message, :chat?, :body => 'exit' do |m|
|
26
|
+
# client.write Blather::Stanza::Message.new(m.from, 'Exiting...')
|
27
|
+
# client.close
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# client.register_handler :message, :chat?, :body do |m|
|
31
|
+
# client.write Blather::Stanza::Message.new(m.from, "You sent: #{m.body}")
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
class Client
|
35
|
+
attr_reader :jid,
|
36
|
+
:roster,
|
37
|
+
:caps
|
38
|
+
|
39
|
+
# Create a new client and set it up
|
40
|
+
#
|
41
|
+
# @param [Blather::JID, #to_s] jid the JID to authorize with
|
42
|
+
# @param [String] password the password to authorize with
|
43
|
+
# @param [String] host if this isn't set it'll be resolved off the JID's
|
44
|
+
# domain
|
45
|
+
# @param [Fixnum, String] port the port to connect to.
|
46
|
+
#
|
47
|
+
# @return [Blather::Client]
|
48
|
+
def self.setup(jid, password, host = nil, port = nil, certs = nil, connect_timeout = nil)
|
49
|
+
self.new.setup(jid, password, host, port, certs, connect_timeout)
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize # @private
|
53
|
+
@state = :initializing
|
54
|
+
|
55
|
+
@status = Stanza::Presence::Status.new
|
56
|
+
@handlers = {}
|
57
|
+
@tmp_handlers = {}
|
58
|
+
@filters = {:before => [], :after => []}
|
59
|
+
@roster = Roster.new self
|
60
|
+
@caps = Stanza::Capabilities.new
|
61
|
+
|
62
|
+
@handler_queue = GirlFriday::WorkQueue.new :handle_stanza, :size => 5 do |stanza|
|
63
|
+
handle_data stanza
|
64
|
+
end
|
65
|
+
|
66
|
+
setup_initial_handlers
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check whether the client is currently connected.
|
70
|
+
def connected?
|
71
|
+
setup? && !@stream.nil? && !@stream.stopped?
|
72
|
+
end
|
73
|
+
|
74
|
+
# Get the current status. Taken from the `state` attribute of Status
|
75
|
+
def status
|
76
|
+
@status.state
|
77
|
+
end
|
78
|
+
|
79
|
+
# Set the status. Status can be set with either a single value or an array
|
80
|
+
# containing
|
81
|
+
#
|
82
|
+
# [state, message, to].
|
83
|
+
def status=(state)
|
84
|
+
state, msg, to = state
|
85
|
+
|
86
|
+
status = Stanza::Presence::Status.new state, msg
|
87
|
+
status.to = to
|
88
|
+
@status = status unless to
|
89
|
+
|
90
|
+
write status
|
91
|
+
end
|
92
|
+
|
93
|
+
# Start the connection.
|
94
|
+
#
|
95
|
+
# The stream type used is based on the JID. If a node exists it uses
|
96
|
+
# Blather::Stream::Client otherwise Blather::Stream::Component
|
97
|
+
def run
|
98
|
+
raise 'not setup!' unless setup?
|
99
|
+
klass = @setup[0].node ? Blather::Stream::Client : Blather::Stream::Component
|
100
|
+
klass.start self, *@setup
|
101
|
+
end
|
102
|
+
alias_method :connect, :run
|
103
|
+
|
104
|
+
# Register a filter to be run before or after the handler chain is run.
|
105
|
+
#
|
106
|
+
# @param [<:before, :after>] type the filter type
|
107
|
+
# @param [Symbol, nil] handler set the filter on a specific handler
|
108
|
+
# @param [guards] guards take a look at the guards documentation
|
109
|
+
# @yield [Blather::Stanza] stanza the incomming stanza
|
110
|
+
def register_filter(type, handler = nil, *guards, &filter)
|
111
|
+
unless [:before, :after].include?(type)
|
112
|
+
raise "Invalid filter: #{type}. Must be :before or :after"
|
113
|
+
end
|
114
|
+
@filters[type] << [guards, handler, filter]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Register a temporary handler. Temporary handlers are based on the ID of
|
118
|
+
# the JID and live only until a stanza with said ID is received.
|
119
|
+
#
|
120
|
+
# @param [#to_s] id the ID of the stanza that should be handled
|
121
|
+
# @yield [Blather::Stanza] stanza the incomming stanza
|
122
|
+
def register_tmp_handler(id, &handler)
|
123
|
+
@tmp_handlers[id.to_s] = handler
|
124
|
+
end
|
125
|
+
|
126
|
+
# Clear handlers with given guards
|
127
|
+
#
|
128
|
+
# @param [Symbol, nil] type remove filters for a specific handler
|
129
|
+
# @param [guards] guards take a look at the guards documentation
|
130
|
+
def clear_handlers(type, *guards)
|
131
|
+
@handlers[type].delete_if { |g, _| g == guards }
|
132
|
+
end
|
133
|
+
|
134
|
+
# Register a handler
|
135
|
+
#
|
136
|
+
# @param [Symbol, nil] type set the filter on a specific handler
|
137
|
+
# @param [guards] guards take a look at the guards documentation
|
138
|
+
# @yield [Blather::Stanza] stanza the incomming stanza
|
139
|
+
def register_handler(type, *guards, &handler)
|
140
|
+
check_handler type, guards
|
141
|
+
@handlers[type] ||= []
|
142
|
+
@handlers[type] << [guards, handler]
|
143
|
+
end
|
144
|
+
|
145
|
+
# Write data to the stream
|
146
|
+
#
|
147
|
+
# @param [#to_xml, #to_s] stanza the content to send down the wire
|
148
|
+
def write(stanza)
|
149
|
+
self.stream.send(stanza)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Helper that will create a temporary handler for the stanza being sent
|
153
|
+
# before writing it to the stream.
|
154
|
+
#
|
155
|
+
# client.write_with_handler(stanza) { |s| "handle stanza here" }
|
156
|
+
#
|
157
|
+
# is equivalent to:
|
158
|
+
#
|
159
|
+
# client.register_tmp_handler(stanza.id) { |s| "handle stanza here" }
|
160
|
+
# client.write stanza
|
161
|
+
#
|
162
|
+
# @param [Blather::Stanza] stanza the stanza to send down the wire
|
163
|
+
# @yield [Blather::Stanza] stanza the reply stanza
|
164
|
+
def write_with_handler(stanza, &handler)
|
165
|
+
register_tmp_handler stanza.id, &handler
|
166
|
+
write stanza
|
167
|
+
end
|
168
|
+
|
169
|
+
# Close the connection
|
170
|
+
def close
|
171
|
+
self.stream.close_connection_after_writing
|
172
|
+
end
|
173
|
+
|
174
|
+
# @private
|
175
|
+
def post_init(stream, jid = nil)
|
176
|
+
@stream = stream
|
177
|
+
@jid = JID.new(jid) if jid
|
178
|
+
self.jid.node ? client_post_init : ready!
|
179
|
+
end
|
180
|
+
|
181
|
+
# @private
|
182
|
+
def unbind
|
183
|
+
call_handler_for(:disconnected, nil) || (EM.reactor_running? && EM.stop)
|
184
|
+
end
|
185
|
+
|
186
|
+
# @private
|
187
|
+
def receive_data(stanza)
|
188
|
+
@handler_queue << stanza
|
189
|
+
end
|
190
|
+
|
191
|
+
def handle_data(stanza)
|
192
|
+
catch(:halt) do
|
193
|
+
run_filters :before, stanza
|
194
|
+
handle_stanza stanza
|
195
|
+
run_filters :after, stanza
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# @private
|
200
|
+
def setup?
|
201
|
+
@setup.is_a? Array
|
202
|
+
end
|
203
|
+
|
204
|
+
# @private
|
205
|
+
def setup(jid, password, host = nil, port = nil, certs = nil, connect_timeout = nil)
|
206
|
+
@jid = JID.new(jid)
|
207
|
+
@setup = [@jid, password]
|
208
|
+
@setup << host
|
209
|
+
@setup << port
|
210
|
+
@setup << certs
|
211
|
+
@setup << connect_timeout
|
212
|
+
self
|
213
|
+
end
|
214
|
+
|
215
|
+
protected
|
216
|
+
|
217
|
+
def stream
|
218
|
+
@stream || raise('Stream not ready!')
|
219
|
+
end
|
220
|
+
|
221
|
+
def check_handler(type, guards)
|
222
|
+
Blather.logger.warn "Handler for type \"#{type}\" will never be called as it's not a registered type" unless current_handlers.include?(type)
|
223
|
+
check_guards guards
|
224
|
+
end
|
225
|
+
|
226
|
+
def current_handlers
|
227
|
+
[:ready, :disconnected] + Stanza.handler_list + BlatherError.handler_list
|
228
|
+
end
|
229
|
+
|
230
|
+
def setup_initial_handlers
|
231
|
+
register_handler :error do |err|
|
232
|
+
raise err
|
233
|
+
end
|
234
|
+
|
235
|
+
# register_handler :iq, :type => [:get, :set] do |iq|
|
236
|
+
# write StanzaError.new(iq, 'service-unavailable', :cancel).to_node
|
237
|
+
# end
|
238
|
+
|
239
|
+
register_handler :ping, :type => :get do |ping|
|
240
|
+
write ping.reply
|
241
|
+
end
|
242
|
+
|
243
|
+
register_handler :status do |status|
|
244
|
+
roster[status.from].status = status if roster[status.from]
|
245
|
+
nil
|
246
|
+
end
|
247
|
+
|
248
|
+
register_handler :roster do |node|
|
249
|
+
roster.process node
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def ready!
|
254
|
+
@state = :ready
|
255
|
+
call_handler_for :ready, nil
|
256
|
+
end
|
257
|
+
|
258
|
+
def client_post_init
|
259
|
+
write_with_handler Stanza::Iq::Roster.new do |node|
|
260
|
+
roster.process node
|
261
|
+
write @status
|
262
|
+
ready!
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def run_filters(type, stanza)
|
267
|
+
@filters[type].each do |guards, handler, filter|
|
268
|
+
next if handler && !stanza.handler_hierarchy.include?(handler)
|
269
|
+
catch(:pass) { call_handler filter, guards, stanza }
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def handle_stanza(stanza)
|
274
|
+
if handler = @tmp_handlers.delete(stanza.id)
|
275
|
+
handler.call stanza
|
276
|
+
else
|
277
|
+
stanza.handler_hierarchy.each do |type|
|
278
|
+
break if call_handler_for(type, stanza)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def call_handler_for(type, stanza)
|
284
|
+
return unless handler = @handlers[type]
|
285
|
+
handler.find do |guards, handler|
|
286
|
+
catch(:pass) { call_handler handler, guards, stanza }
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def call_handler(handler, guards, stanza)
|
291
|
+
if guards.first.respond_to?(:to_str)
|
292
|
+
result = stanza.find(*guards)
|
293
|
+
handler.call(stanza, result) unless result.empty?
|
294
|
+
else
|
295
|
+
handler.call(stanza) unless guarded?(guards, stanza)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# If any of the guards returns FALSE this returns true
|
300
|
+
# the logic is reversed to allow short circuiting
|
301
|
+
# (why would anyone want to loop over more values than necessary?)
|
302
|
+
#
|
303
|
+
# @private
|
304
|
+
def guarded?(guards, stanza)
|
305
|
+
guards.find do |guard|
|
306
|
+
case guard
|
307
|
+
when Symbol
|
308
|
+
!stanza.__send__(guard)
|
309
|
+
when Array
|
310
|
+
# return FALSE if any item is TRUE
|
311
|
+
!guard.detect { |condition| !guarded?([condition], stanza) }
|
312
|
+
when Hash
|
313
|
+
# return FALSE unless any inequality is found
|
314
|
+
guard.find do |method, test|
|
315
|
+
value = stanza.__send__(method)
|
316
|
+
# last_match is the only method found unique to Regexp classes
|
317
|
+
if test.class.respond_to?(:last_match)
|
318
|
+
!(test =~ value.to_s)
|
319
|
+
elsif test.is_a?(Array)
|
320
|
+
!test.include? value
|
321
|
+
else
|
322
|
+
test != value
|
323
|
+
end
|
324
|
+
end
|
325
|
+
when Proc
|
326
|
+
!guard.call(stanza)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def check_guards(guards)
|
332
|
+
guards.each do |guard|
|
333
|
+
case guard
|
334
|
+
when Array
|
335
|
+
guard.each { |g| check_guards([g]) }
|
336
|
+
when Symbol, Proc, Hash, String
|
337
|
+
nil
|
338
|
+
else
|
339
|
+
raise "Bad guard: #{guard.inspect}"
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end # Client
|
344
|
+
|
345
|
+
end # Blather
|
@@ -0,0 +1,320 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'client')
|
2
|
+
|
3
|
+
module Blather
|
4
|
+
|
5
|
+
# # Blather DSL
|
6
|
+
#
|
7
|
+
# The DSL is a set of methods that enables you to write cleaner code. Being a
|
8
|
+
# module means it can be included in or extend any class you may want to
|
9
|
+
# create.
|
10
|
+
#
|
11
|
+
# Every stanza handler is registered as a method on the DSL.
|
12
|
+
#
|
13
|
+
# @example Include the DSL in the top level namespace.
|
14
|
+
#
|
15
|
+
# require 'blather/client'
|
16
|
+
# when_ready { puts "Connected ! send messages to #{jid.stripped}." }
|
17
|
+
#
|
18
|
+
# subscription :request? do |s|
|
19
|
+
# write_to_stream s.approve!
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# message :chat?, :body => 'exit' do |m|
|
23
|
+
# say m.from, 'Exiting ...'
|
24
|
+
# shutdown
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# message :chat?, :body do |m|
|
28
|
+
# say m.from, "You sent: #{m.body}"
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# @example Set the DSL to its own namespace.
|
32
|
+
#
|
33
|
+
# require 'blather/client/dsl'
|
34
|
+
# module Echo
|
35
|
+
# extend Blather::DSL
|
36
|
+
#
|
37
|
+
# when_ready { puts "Connected ! send messages to #{jid.stripped}." }
|
38
|
+
#
|
39
|
+
# subscription :request? do |s|
|
40
|
+
# write_to_stream s.approve!
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# message :chat?, :body => 'exit' do |m|
|
44
|
+
# say m.from, 'Exiting ...'
|
45
|
+
# shutdown
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# message :chat?, :body do |m|
|
49
|
+
# say m.from, "You sent: #{m.body}"
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# Echo.setup 'foo@bar.com', 'foobar'
|
54
|
+
#
|
55
|
+
# EM.run { Echo.run }
|
56
|
+
#
|
57
|
+
# @example Create a class out of it
|
58
|
+
#
|
59
|
+
# require 'blather/client/dsl'
|
60
|
+
# class Echo
|
61
|
+
# include Blather::DSL
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# echo = Echo.new
|
65
|
+
# echo.setup 'foo@bar.com', 'foobar'
|
66
|
+
# echo.when_ready { puts "Connected ! send messages to #{jid.stripped}." }
|
67
|
+
#
|
68
|
+
# echo.subscription :request? do |s|
|
69
|
+
# write_to_stream s.approve!
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# echo.message :chat?, :body => 'exit' do |m|
|
73
|
+
# say m.from, 'Exiting ...'
|
74
|
+
# shutdown
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# echo.message :chat?, :body do |m|
|
78
|
+
# say m.from, "You sent: #{m.body}"
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# EM.run { echo.run }
|
82
|
+
#
|
83
|
+
module DSL
|
84
|
+
|
85
|
+
autoload :PubSub, File.expand_path(File.join(File.dirname(__FILE__), *%w[dsl pubsub]))
|
86
|
+
|
87
|
+
def self.append_features(o)
|
88
|
+
Blather::Stanza.handler_list.each do |handler_name|
|
89
|
+
o.__send__ :remove_method, handler_name if !o.is_a?(Class) && o.method_defined?(handler_name)
|
90
|
+
end
|
91
|
+
super
|
92
|
+
end
|
93
|
+
|
94
|
+
# The actual client connection
|
95
|
+
#
|
96
|
+
# @return [Blather::Client]
|
97
|
+
def client
|
98
|
+
@client ||= Client.new
|
99
|
+
end
|
100
|
+
module_function :client
|
101
|
+
|
102
|
+
# A pubsub helper
|
103
|
+
#
|
104
|
+
# @return [Blather::PubSub]
|
105
|
+
def pubsub
|
106
|
+
@pubsub ||= PubSub.new client, jid.domain
|
107
|
+
end
|
108
|
+
|
109
|
+
# Push data to the stream
|
110
|
+
# This works such that it can be chained:
|
111
|
+
# self << stanza1 << stanza2 << "raw data"
|
112
|
+
#
|
113
|
+
# @param [#to_xml, #to_s] stanza data to send down the wire
|
114
|
+
# @return [self]
|
115
|
+
def <<(stanza)
|
116
|
+
client.write stanza
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
# Prepare server settings
|
121
|
+
#
|
122
|
+
# @param [#to_s] jid the JID to authenticate with
|
123
|
+
# @param [#to_s] password the password to authenticate with
|
124
|
+
# @param [String] host (optional) the host to connect to (can be an IP). If
|
125
|
+
# this is `nil` the domain on the JID will be used
|
126
|
+
# @param [Fixnum, String] (optional) port the port to connect on
|
127
|
+
# @param [Fixnum] (optional) connection_timeout the time to wait for connection to succeed before timing out
|
128
|
+
def setup(jid, password, host = nil, port = nil, certs = nil, connection_timeout = nil)
|
129
|
+
client.setup(jid, password, host, port, certs, connection_timeout)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Connect to the server. Must be run in the EventMachine reactor
|
133
|
+
def run
|
134
|
+
client.run
|
135
|
+
end
|
136
|
+
|
137
|
+
# Shutdown the connection.
|
138
|
+
# Flushes the write buffer then stops EventMachine
|
139
|
+
def shutdown
|
140
|
+
client.close
|
141
|
+
end
|
142
|
+
|
143
|
+
# Setup a before filter
|
144
|
+
#
|
145
|
+
# @param [Symbol] handler (optional) the stanza handler the filter should
|
146
|
+
# run before
|
147
|
+
# @param [guards] guards (optional) a set of guards to check the stanza
|
148
|
+
# against
|
149
|
+
# @yield [Blather::Stanza] stanza
|
150
|
+
def before(handler = nil, *guards, &block)
|
151
|
+
client.register_filter :before, handler, *guards, &block
|
152
|
+
end
|
153
|
+
|
154
|
+
# Setup an after filter
|
155
|
+
#
|
156
|
+
# @param [Symbol] handler (optional) the stanza handler the filter should
|
157
|
+
# run after
|
158
|
+
# @param [guards] guards (optional) a set of guards to check the stanza
|
159
|
+
# against
|
160
|
+
# @yield [Blather::Stanza] stanza
|
161
|
+
def after(handler = nil, *guards, &block)
|
162
|
+
client.register_filter :after, handler, *guards, &block
|
163
|
+
end
|
164
|
+
|
165
|
+
# Set handler for a stanza type
|
166
|
+
#
|
167
|
+
# @param [Symbol] handler the stanza type it should handle
|
168
|
+
# @param [guards] guards (optional) a set of guards to check the stanza
|
169
|
+
# against
|
170
|
+
# @yield [Blather::Stanza] stanza
|
171
|
+
def handle(handler, *guards, &block)
|
172
|
+
client.register_handler handler, *guards, &block
|
173
|
+
end
|
174
|
+
|
175
|
+
# Wrapper for "handle :ready" (just a bit of syntactic sugar)
|
176
|
+
#
|
177
|
+
# This is run after the connection has been completely setup
|
178
|
+
def when_ready(&block)
|
179
|
+
handle :ready, &block
|
180
|
+
end
|
181
|
+
|
182
|
+
# Wrapper for "handle :disconnected"
|
183
|
+
#
|
184
|
+
# This is run after the connection has been shut down.
|
185
|
+
#
|
186
|
+
# @example Reconnect after a disconnection
|
187
|
+
# disconnected { client.run }
|
188
|
+
def disconnected(&block)
|
189
|
+
handle :disconnected, &block
|
190
|
+
end
|
191
|
+
|
192
|
+
# Set current status
|
193
|
+
#
|
194
|
+
# @param [Blather::Stanza::Presence::State::VALID_STATES] state the current
|
195
|
+
# state
|
196
|
+
# @param [#to_s] msg the status message to use
|
197
|
+
def set_status(state = nil, msg = nil)
|
198
|
+
client.status = state, msg
|
199
|
+
end
|
200
|
+
|
201
|
+
# Direct access to the roster
|
202
|
+
#
|
203
|
+
# @return [Blather::Roster]
|
204
|
+
def my_roster
|
205
|
+
client.roster
|
206
|
+
end
|
207
|
+
|
208
|
+
# Write data to the stream
|
209
|
+
#
|
210
|
+
# @param [#to_xml, #to_s] stanza the data to send down the wire.
|
211
|
+
def write_to_stream(stanza)
|
212
|
+
client.write stanza
|
213
|
+
end
|
214
|
+
|
215
|
+
# Helper method to join a MUC room
|
216
|
+
#
|
217
|
+
# @overload join(room_jid, nickname)
|
218
|
+
# @param [Blather::JID, #to_s] room the JID of the room to join
|
219
|
+
# @param [#to_s] nickname the nickname to join the room as
|
220
|
+
# @overload join(room_jid, nickname)
|
221
|
+
# @param [#to_s] room the name of the room to join
|
222
|
+
# @param [Blather::JID, #to_s] service the service domain the room is hosted at
|
223
|
+
# @param [#to_s] nickname the nickname to join the room as
|
224
|
+
def join(room, service, nickname = nil)
|
225
|
+
join = Blather::Stanza::Presence::MUC.new
|
226
|
+
join.to = if nickname
|
227
|
+
"#{room}@#{service}/#{nickname}"
|
228
|
+
else
|
229
|
+
"#{room}/#{service}"
|
230
|
+
end
|
231
|
+
client.write join
|
232
|
+
end
|
233
|
+
|
234
|
+
# Helper method to make sending basic messages easier
|
235
|
+
#
|
236
|
+
# @param [Blather::JID, #to_s] to the JID of the message recipient
|
237
|
+
# @param [#to_s] msg the message to send
|
238
|
+
# @param [#to_sym] the stanza method to use
|
239
|
+
def say(to, msg, using = :chat)
|
240
|
+
client.write Blather::Stanza::Message.new(to, msg, using)
|
241
|
+
end
|
242
|
+
|
243
|
+
# The JID according to the server
|
244
|
+
#
|
245
|
+
# @return [Blather::JID]
|
246
|
+
def jid
|
247
|
+
client.jid
|
248
|
+
end
|
249
|
+
|
250
|
+
# Halt the handler chain
|
251
|
+
#
|
252
|
+
# Use this to stop the propogation of the stanza though the handler chain.
|
253
|
+
#
|
254
|
+
# @example Ignore all IQ stanzas
|
255
|
+
#
|
256
|
+
# before(:iq) { halt }
|
257
|
+
def halt
|
258
|
+
throw :halt
|
259
|
+
end
|
260
|
+
|
261
|
+
# Pass responsibility to the next handler
|
262
|
+
#
|
263
|
+
# Use this to jump out of the current handler and let the next registered
|
264
|
+
# handler take care of the stanza
|
265
|
+
#
|
266
|
+
# @example Pass a message to the next handler
|
267
|
+
#
|
268
|
+
# This is contrive and should be handled with guards, but pass a message
|
269
|
+
# to the next handler based on the content
|
270
|
+
#
|
271
|
+
# message { |s| puts "message caught" }
|
272
|
+
# message { |s| pass if s.body =~ /pass along/ }
|
273
|
+
def pass
|
274
|
+
throw :pass
|
275
|
+
end
|
276
|
+
|
277
|
+
# Request items or info from an entity
|
278
|
+
# discover (items|info), [jid], [node] do |response|
|
279
|
+
# end
|
280
|
+
def discover(what, who, where, &callback)
|
281
|
+
stanza = Blather::Stanza.class_from_registration(:query, "http://jabber.org/protocol/disco##{what}").new
|
282
|
+
stanza.to = who
|
283
|
+
stanza.node = where
|
284
|
+
|
285
|
+
client.register_tmp_handler stanza.id, &callback
|
286
|
+
client.write stanza
|
287
|
+
end
|
288
|
+
|
289
|
+
# Set the capabilities of the client
|
290
|
+
#
|
291
|
+
# @param [String] node the URI
|
292
|
+
# @param [Array<Hash>] identities an array of identities
|
293
|
+
# @param [Array<Hash>] features an array of features
|
294
|
+
def set_caps(node, identities, features)
|
295
|
+
client.caps.node = node
|
296
|
+
client.caps.identities = identities
|
297
|
+
client.caps.features = features
|
298
|
+
end
|
299
|
+
|
300
|
+
# Send capabilities to the server
|
301
|
+
def send_caps
|
302
|
+
client.register_handler :disco_info, :type => :get, :node => client.caps.node do |s|
|
303
|
+
r = client.caps.dup
|
304
|
+
r.to = s.from
|
305
|
+
r.id = s.id
|
306
|
+
client.write r
|
307
|
+
end
|
308
|
+
client.write client.caps.c
|
309
|
+
end
|
310
|
+
|
311
|
+
# Generate a method for every stanza handler that exists.
|
312
|
+
Blather::Stanza.handler_list.each do |handler_name|
|
313
|
+
module_eval <<-METHOD, __FILE__, __LINE__
|
314
|
+
def #{handler_name}(*args, &callback)
|
315
|
+
handle :#{handler_name}, *args, &callback
|
316
|
+
end
|
317
|
+
METHOD
|
318
|
+
end
|
319
|
+
end # DSL
|
320
|
+
end # Blather
|