shingara-blather 0.4.8
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.md +162 -0
- data/examples/echo.rb +18 -0
- data/examples/execute.rb +16 -0
- data/examples/ping_pong.rb +37 -0
- data/examples/print_hierarchy.rb +76 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/stream_only.rb +27 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/lib/blather/client/client.rb +310 -0
- data/lib/blather/client/dsl/pubsub.rb +170 -0
- data/lib/blather/client/dsl.rb +264 -0
- data/lib/blather/client.rb +87 -0
- data/lib/blather/core_ext/nokogiri.rb +40 -0
- data/lib/blather/errors/sasl_error.rb +43 -0
- data/lib/blather/errors/stanza_error.rb +107 -0
- data/lib/blather/errors/stream_error.rb +82 -0
- data/lib/blather/errors.rb +69 -0
- data/lib/blather/jid.rb +142 -0
- data/lib/blather/roster.rb +111 -0
- data/lib/blather/roster_item.rb +122 -0
- data/lib/blather/stanza/disco/disco_info.rb +176 -0
- data/lib/blather/stanza/disco/disco_items.rb +132 -0
- data/lib/blather/stanza/disco.rb +25 -0
- data/lib/blather/stanza/iq/query.rb +53 -0
- data/lib/blather/stanza/iq/roster.rb +179 -0
- data/lib/blather/stanza/iq.rb +138 -0
- data/lib/blather/stanza/message.rb +332 -0
- data/lib/blather/stanza/presence/status.rb +212 -0
- data/lib/blather/stanza/presence/subscription.rb +101 -0
- data/lib/blather/stanza/presence.rb +163 -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 +123 -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 +134 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +81 -0
- data/lib/blather/stanza/pubsub/unsubscribe.rb +68 -0
- data/lib/blather/stanza/pubsub.rb +129 -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/pubsub_owner.rb +51 -0
- data/lib/blather/stanza.rb +149 -0
- data/lib/blather/stream/client.rb +31 -0
- data/lib/blather/stream/component.rb +38 -0
- data/lib/blather/stream/features/resource.rb +63 -0
- data/lib/blather/stream/features/sasl.rb +187 -0
- data/lib/blather/stream/features/session.rb +44 -0
- data/lib/blather/stream/features/tls.rb +28 -0
- data/lib/blather/stream/features.rb +53 -0
- data/lib/blather/stream/parser.rb +102 -0
- data/lib/blather/stream.rb +231 -0
- data/lib/blather/xmpp_node.rb +218 -0
- data/lib/blather.rb +78 -0
- data/spec/blather/client/client_spec.rb +559 -0
- data/spec/blather/client/dsl/pubsub_spec.rb +462 -0
- data/spec/blather/client/dsl_spec.rb +143 -0
- data/spec/blather/core_ext/nokogiri_spec.rb +83 -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/jid_spec.rb +87 -0
- data/spec/blather/roster_item_spec.rb +96 -0
- data/spec/blather/roster_spec.rb +103 -0
- data/spec/blather/stanza/discos/disco_info_spec.rb +226 -0
- data/spec/blather/stanza/discos/disco_items_spec.rb +148 -0
- data/spec/blather/stanza/iq/query_spec.rb +64 -0
- data/spec/blather/stanza/iq/roster_spec.rb +140 -0
- data/spec/blather/stanza/iq_spec.rb +45 -0
- data/spec/blather/stanza/message_spec.rb +132 -0
- data/spec/blather/stanza/presence/status_spec.rb +132 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +105 -0
- data/spec/blather/stanza/presence_spec.rb +66 -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 +84 -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 +61 -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 +67 -0
- data/spec/blather/stanza_spec.rb +116 -0
- data/spec/blather/stream/client_spec.rb +1011 -0
- data/spec/blather/stream/component_spec.rb +95 -0
- data/spec/blather/stream/parser_spec.rb +145 -0
- data/spec/blather/xmpp_node_spec.rb +231 -0
- data/spec/fixtures/pubsub.rb +311 -0
- data/spec/spec_helper.rb +43 -0
- metadata +249 -0
@@ -0,0 +1,264 @@
|
|
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
|
+
# def self.run
|
37
|
+
# client.run
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# when_ready { puts "Connected ! send messages to #{jid.stripped}." }
|
41
|
+
#
|
42
|
+
# subscription :request? do |s|
|
43
|
+
# write_to_stream s.approve!
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# message :chat?, :body => 'exit' do |m|
|
47
|
+
# say m.from, 'Exiting ...'
|
48
|
+
# shutdown
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# message :chat?, :body do |m|
|
52
|
+
# say m.from, "You sent: #{m.body}"
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# EM.run { Echo.run }
|
57
|
+
#
|
58
|
+
# @example Create a class out of it
|
59
|
+
#
|
60
|
+
# require 'blather/client/dsl'
|
61
|
+
# class Echo
|
62
|
+
# include Blather::DSL
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# echo = Echo.new
|
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.client.run }
|
82
|
+
module DSL
|
83
|
+
|
84
|
+
autoload :PubSub, File.expand_path(File.join(File.dirname(__FILE__), *%w[dsl pubsub]))
|
85
|
+
|
86
|
+
# The actual client connection
|
87
|
+
#
|
88
|
+
# @return [Blather::Client]
|
89
|
+
def client
|
90
|
+
@client ||= Client.new
|
91
|
+
end
|
92
|
+
module_function :client
|
93
|
+
|
94
|
+
# A pubsub helper
|
95
|
+
#
|
96
|
+
# @return [Blather::PubSub]
|
97
|
+
def pubsub
|
98
|
+
@pubsub ||= PubSub.new client, jid.domain
|
99
|
+
end
|
100
|
+
|
101
|
+
# Push data to the stream
|
102
|
+
# This works such that it can be chained:
|
103
|
+
# self << stanza1 << stanza2 << "raw data"
|
104
|
+
#
|
105
|
+
# @param [#to_xml, #to_s] stanza data to send down the wire
|
106
|
+
# @return [self]
|
107
|
+
def <<(stanza)
|
108
|
+
client.write stanza
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
# Prepare server settings
|
113
|
+
#
|
114
|
+
# @param [#to_s] jid the JID to authenticate with
|
115
|
+
# @param [#to_s] password the password to authenticate with
|
116
|
+
# @param [String] host (optional) the host to connect to (can be an IP). If
|
117
|
+
# this is `nil` the domain on the JID will be used
|
118
|
+
# @param [Fixnum, String] (optional) port the port to connect on
|
119
|
+
def setup(jid, password, host = nil, port = nil)
|
120
|
+
client.setup(jid, password, host, port)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Shutdown the connection.
|
124
|
+
# Flushes the write buffer then stops EventMachine
|
125
|
+
def shutdown
|
126
|
+
client.close
|
127
|
+
end
|
128
|
+
|
129
|
+
# Setup a before filter
|
130
|
+
#
|
131
|
+
# @param [Symbol] handler (optional) the stanza handler the filter should
|
132
|
+
# run before
|
133
|
+
# @param [guards] guards (optional) a set of guards to check the stanza
|
134
|
+
# against
|
135
|
+
# @yield [Blather::Stanza] stanza
|
136
|
+
def before(handler = nil, *guards, &block)
|
137
|
+
client.register_filter :before, handler, *guards, &block
|
138
|
+
end
|
139
|
+
|
140
|
+
# Setup an after filter
|
141
|
+
#
|
142
|
+
# @param [Symbol] handler (optional) the stanza handler the filter should
|
143
|
+
# run after
|
144
|
+
# @param [guards] guards (optional) a set of guards to check the stanza
|
145
|
+
# against
|
146
|
+
# @yield [Blather::Stanza] stanza
|
147
|
+
def after(handler = nil, *guards, &block)
|
148
|
+
client.register_filter :after, handler, *guards, &block
|
149
|
+
end
|
150
|
+
|
151
|
+
# Set handler for a stanza type
|
152
|
+
#
|
153
|
+
# @param [Symbol] handler the stanza type it should handle
|
154
|
+
# @param [guards] guards (optional) a set of guards to check the stanza
|
155
|
+
# against
|
156
|
+
# @yield [Blather::Stanza] stanza
|
157
|
+
def handle(handler, *guards, &block)
|
158
|
+
client.register_handler handler, *guards, &block
|
159
|
+
end
|
160
|
+
|
161
|
+
# Wrapper for "handle :ready" (just a bit of syntactic sugar)
|
162
|
+
#
|
163
|
+
# This is run after the connection has been completely setup
|
164
|
+
def when_ready(&block)
|
165
|
+
handle :ready, &block
|
166
|
+
end
|
167
|
+
|
168
|
+
# Wrapper for "handle :disconnected"
|
169
|
+
#
|
170
|
+
# This is run after the connection has been shut down.
|
171
|
+
#
|
172
|
+
# @example Reconnect after a disconnection
|
173
|
+
# disconnected { client.run }
|
174
|
+
def disconnected(&block)
|
175
|
+
handle :disconnected, &block
|
176
|
+
end
|
177
|
+
|
178
|
+
# Set current status
|
179
|
+
#
|
180
|
+
# @param [Blather::Stanza::Presence::State::VALID_STATES] state the current
|
181
|
+
# state
|
182
|
+
# @param [#to_s] msg the status message to use
|
183
|
+
def set_status(state = nil, msg = nil)
|
184
|
+
client.status = state, msg
|
185
|
+
end
|
186
|
+
|
187
|
+
# Direct access to the roster
|
188
|
+
#
|
189
|
+
# @return [Blather::Roster]
|
190
|
+
def my_roster
|
191
|
+
client.roster
|
192
|
+
end
|
193
|
+
|
194
|
+
# Write data to the stream
|
195
|
+
#
|
196
|
+
# @param [#to_xml, #to_s] stanza the data to send down the wire.
|
197
|
+
def write_to_stream(stanza)
|
198
|
+
client.write stanza
|
199
|
+
end
|
200
|
+
|
201
|
+
# Helper method to make sending basic messages easier
|
202
|
+
#
|
203
|
+
# @param [Blather::JID, #to_s] to the JID of the message recipient
|
204
|
+
# @param [#to_s] msg the message to send
|
205
|
+
def say(to, msg)
|
206
|
+
client.write Blather::Stanza::Message.new(to, msg)
|
207
|
+
end
|
208
|
+
|
209
|
+
# The JID according to the server
|
210
|
+
#
|
211
|
+
# @return [Blather::JID]
|
212
|
+
def jid
|
213
|
+
client.jid
|
214
|
+
end
|
215
|
+
|
216
|
+
# Halt the handler chain
|
217
|
+
#
|
218
|
+
# Use this to stop the propogation of the stanza though the handler chain.
|
219
|
+
#
|
220
|
+
# @example
|
221
|
+
# Ignore all IQ stanzas
|
222
|
+
#
|
223
|
+
# before(:iq) { halt }
|
224
|
+
def halt
|
225
|
+
throw :halt
|
226
|
+
end
|
227
|
+
|
228
|
+
# Pass responsibility to the next handler
|
229
|
+
#
|
230
|
+
# Use this to jump out of the current handler and let the next registered
|
231
|
+
# handler take care of the stanza
|
232
|
+
#
|
233
|
+
# @example
|
234
|
+
# This is contrive and should be handled with guards, but pass a message
|
235
|
+
# to the next handler based on the content
|
236
|
+
#
|
237
|
+
# message { |s| puts "message caught" }
|
238
|
+
# message { |s| pass if s.body =~ /pass along/ }
|
239
|
+
def pass
|
240
|
+
throw :pass
|
241
|
+
end
|
242
|
+
|
243
|
+
# Request items or info from an entity
|
244
|
+
# discover (items|info), [jid], [node] do |response|
|
245
|
+
# end
|
246
|
+
def discover(what, who, where, &callback)
|
247
|
+
stanza = Blather::Stanza.class_from_registration(:query, "http://jabber.org/protocol/disco##{what}").new
|
248
|
+
stanza.to = who
|
249
|
+
stanza.node = where
|
250
|
+
|
251
|
+
client.register_tmp_handler stanza.id, &callback
|
252
|
+
client.write stanza
|
253
|
+
end
|
254
|
+
|
255
|
+
# Generate a method for every stanza handler that exists.
|
256
|
+
Blather::Stanza.handler_list.each do |handler_name|
|
257
|
+
module_eval <<-METHOD, __FILE__, __LINE__
|
258
|
+
def #{handler_name}(*args, &callback)
|
259
|
+
handle :#{handler_name}, *args, &callback
|
260
|
+
end
|
261
|
+
METHOD
|
262
|
+
end
|
263
|
+
end # DSL
|
264
|
+
end # Blather
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require File.join(File.dirname(__FILE__), *%w[client dsl])
|
3
|
+
|
4
|
+
include Blather::DSL
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
optparse = OptionParser.new do |opts|
|
8
|
+
opts.banner = "Run with #{$0} [options] user@server/resource password [host] [port]"
|
9
|
+
|
10
|
+
opts.on('-D', '--debug', 'Run in debug mode (you will see all XMPP communication)') do
|
11
|
+
options[:debug] = true
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.on('-d', '--daemonize', 'Daemonize the process') do |daemonize|
|
15
|
+
options[:daemonize] = daemonize
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on('--pid=[PID]', 'Write the PID to this file') do |pid|
|
19
|
+
if !File.writable?(File.dirname(pid))
|
20
|
+
$stderr.puts "Unable to write log file to #{pid}"
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
options[:pid] = pid
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on('--log=[LOG]', 'Write to the [LOG] file instead of stdout/stderr') do |log|
|
27
|
+
if !File.writable?(File.dirname(log))
|
28
|
+
$stderr.puts "Unable to write log file to #{log}"
|
29
|
+
exit 1
|
30
|
+
end
|
31
|
+
options[:log] = log
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
35
|
+
puts opts
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
40
|
+
require 'yaml'
|
41
|
+
version = YAML.load_file File.join(File.dirname(__FILE__), %w[.. .. VERSION.yml])
|
42
|
+
puts "Blather v#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
end
|
46
|
+
optparse.parse!
|
47
|
+
|
48
|
+
at_exit do
|
49
|
+
unless client.setup?
|
50
|
+
if ARGV.length < 2
|
51
|
+
puts optparse
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
client.setup(*ARGV)
|
55
|
+
end
|
56
|
+
|
57
|
+
def run(options)
|
58
|
+
$stdin.reopen "/dev/null"
|
59
|
+
|
60
|
+
if options[:log]
|
61
|
+
log = File.new(options[:log], 'a')
|
62
|
+
log.sync = options[:debug]
|
63
|
+
$stdout.reopen log
|
64
|
+
$stderr.reopen $stdout
|
65
|
+
end
|
66
|
+
|
67
|
+
Blather.logger.level = Logger::DEBUG if options[:debug]
|
68
|
+
|
69
|
+
trap(:INT) { EM.stop }
|
70
|
+
trap(:TERM) { EM.stop }
|
71
|
+
EM.run { client.run }
|
72
|
+
end
|
73
|
+
|
74
|
+
if options[:daemonize]
|
75
|
+
pid = fork do
|
76
|
+
Process.setsid
|
77
|
+
exit if fork
|
78
|
+
File.open(options[:pid], 'w') { |f| f << Process.pid } if options[:pid]
|
79
|
+
run options
|
80
|
+
FileUtils.rm(options[:pid]) if options[:pid]
|
81
|
+
end
|
82
|
+
::Process.detach pid
|
83
|
+
exit
|
84
|
+
else
|
85
|
+
run options
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Nokogiri
|
2
|
+
module XML
|
3
|
+
|
4
|
+
class Node
|
5
|
+
# Alias #name to #element_name so we can use #name in an XMPP Stanza context
|
6
|
+
alias_method :element_name, :name
|
7
|
+
alias_method :element_name=, :name=
|
8
|
+
|
9
|
+
alias_method :attr_set, :[]= # :nodoc:
|
10
|
+
# Override Nokogiri's attribute setter to add the ability to kill an attribute
|
11
|
+
# by setting it to nil and to be able to lookup an attribute by symbol
|
12
|
+
#
|
13
|
+
# @param [#to_s] name the name of the attribute
|
14
|
+
# @param [#to_s, nil] value the new value or nil to remove it
|
15
|
+
def []=(name, value)
|
16
|
+
name = name.to_s
|
17
|
+
value.nil? ? remove_attribute(name) : attr_set(name, value.to_s)
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :nokogiri_xpath, :xpath
|
21
|
+
# Override Nokogiri's #xpath method to add the ability to use symbols for lookup
|
22
|
+
# and namespace designation
|
23
|
+
def xpath(*paths)
|
24
|
+
paths[0] = paths[0].to_s
|
25
|
+
if paths.size > 1 && (namespaces = paths.pop).is_a?(Hash)
|
26
|
+
paths << namespaces.inject({}) { |h,v| h[v[0].to_s] = v[1]; h }
|
27
|
+
end
|
28
|
+
nokogiri_xpath *paths
|
29
|
+
end
|
30
|
+
alias_method :find, :xpath
|
31
|
+
|
32
|
+
# Return the first element at a specified xpath
|
33
|
+
# @see #xpath
|
34
|
+
def find_first(*paths)
|
35
|
+
xpath(*paths).first
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end #XML
|
40
|
+
end #Blather
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Blather
|
2
|
+
|
3
|
+
# General SASL Errors
|
4
|
+
# Check #name for the error name
|
5
|
+
#
|
6
|
+
# @handler :sasl_error
|
7
|
+
class SASLError < BlatherError
|
8
|
+
SASL_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-sasl'
|
9
|
+
|
10
|
+
class_inheritable_accessor :err_name
|
11
|
+
# @private
|
12
|
+
@@registrations = {}
|
13
|
+
|
14
|
+
register :sasl_error
|
15
|
+
|
16
|
+
# Import the stanza
|
17
|
+
#
|
18
|
+
# @param [Blather::XMPPNode] node the error node
|
19
|
+
# @return [Blather::SASLError]
|
20
|
+
def self.import(node)
|
21
|
+
self.new node
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create a new SASLError
|
25
|
+
#
|
26
|
+
# @param [Blather::XMPPNode] node the error node
|
27
|
+
def initialize(node)
|
28
|
+
super()
|
29
|
+
@node = node
|
30
|
+
end
|
31
|
+
|
32
|
+
# The actual error name
|
33
|
+
#
|
34
|
+
# @return [Symbol] a symbol representing the error name
|
35
|
+
def name
|
36
|
+
if @node
|
37
|
+
name = @node.find_first('ns:*', :ns => SASL_ERR_NS).element_name
|
38
|
+
name.gsub('-', '_').to_sym
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end # SASLError
|
42
|
+
|
43
|
+
end # Blather
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Blather
|
2
|
+
|
3
|
+
# Stanza errors
|
4
|
+
# RFC3920 Section 9.3 (http://xmpp.org/rfcs/rfc3920.html#stanzas-error)
|
5
|
+
#
|
6
|
+
# @handler :stanza_error
|
7
|
+
class StanzaError < BlatherError
|
8
|
+
STANZA_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-stanzas'
|
9
|
+
VALID_TYPES = [:cancel, :continue, :modify, :auth, :wait].freeze
|
10
|
+
|
11
|
+
register :stanza_error
|
12
|
+
|
13
|
+
attr_reader :original, :name, :type, :text, :extras
|
14
|
+
|
15
|
+
# Factory method for instantiating the proper class for the error
|
16
|
+
#
|
17
|
+
# @param [Blather::XMPPNode] node the error node to import
|
18
|
+
# @return [Blather::StanzaError]
|
19
|
+
def self.import(node)
|
20
|
+
original = node.copy
|
21
|
+
original.remove_child 'error'
|
22
|
+
|
23
|
+
error_node = node.find_first '//*[local-name()="error"]'
|
24
|
+
|
25
|
+
name = error_node.find_first('child::*[name()!="text"]', STANZA_ERR_NS).element_name
|
26
|
+
type = error_node['type']
|
27
|
+
text = node.find_first 'descendant::*[name()="text"]', STANZA_ERR_NS
|
28
|
+
text = text.content if text
|
29
|
+
|
30
|
+
extras = error_node.find("descendant::*[name()!='text' and name()!='#{name}']").map { |n| n }
|
31
|
+
|
32
|
+
self.new original, name, type, text, extras
|
33
|
+
end
|
34
|
+
|
35
|
+
# Create a new StanzaError
|
36
|
+
#
|
37
|
+
# @param [Blather::XMPPNode] original the original stanza
|
38
|
+
# @param [String] name the error name
|
39
|
+
# @param [#to_s] type the error type as specified in
|
40
|
+
# [RFC3920](http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.3.2)
|
41
|
+
# @param [String, nil] text additional text for the error
|
42
|
+
# @param [Array<Blather::XMPPNode>] extras an array of extra nodes to add
|
43
|
+
def initialize(original, name, type, text = nil, extras = [])
|
44
|
+
@original = original
|
45
|
+
@name = name
|
46
|
+
self.type = type
|
47
|
+
@text = text
|
48
|
+
@extras = extras
|
49
|
+
end
|
50
|
+
|
51
|
+
# Set the error type
|
52
|
+
#
|
53
|
+
# @param [#to_sym] type the new error type. Must be on of
|
54
|
+
# Blather::StanzaError::VALID_TYPES
|
55
|
+
# @see [RFC3920 Section 9.3.2](http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.3.2)
|
56
|
+
def type=(type)
|
57
|
+
type = type.to_sym
|
58
|
+
if !VALID_TYPES.include?(type)
|
59
|
+
raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
|
60
|
+
end
|
61
|
+
@type = type
|
62
|
+
end
|
63
|
+
|
64
|
+
# The error name
|
65
|
+
#
|
66
|
+
# @return [Symbol]
|
67
|
+
def name
|
68
|
+
@name.gsub('-','_').to_sym
|
69
|
+
end
|
70
|
+
|
71
|
+
# Creates an XML node from the error
|
72
|
+
#
|
73
|
+
# @return [Blather::XMPPNode]
|
74
|
+
def to_node
|
75
|
+
node = self.original.reply
|
76
|
+
node.type = 'error'
|
77
|
+
node << (error_node = XMPPNode.new('error'))
|
78
|
+
|
79
|
+
error_node << (err = XMPPNode.new(@name, error_node.document))
|
80
|
+
err.namespace = 'urn:ietf:params:xml:ns:xmpp-stanzas'
|
81
|
+
|
82
|
+
if self.text
|
83
|
+
error_node << (text = XMPPNode.new('text', error_node.document))
|
84
|
+
text.namespace = 'urn:ietf:params:xml:ns:xmpp-stanzas'
|
85
|
+
text.content = self.text
|
86
|
+
end
|
87
|
+
|
88
|
+
self.extras.each { |extra| error_node << extra.dup }
|
89
|
+
node
|
90
|
+
end
|
91
|
+
|
92
|
+
# Convert the object to a proper node then convert it to a string
|
93
|
+
#
|
94
|
+
# @return [String]
|
95
|
+
def to_xml
|
96
|
+
to_node.to_s
|
97
|
+
end
|
98
|
+
|
99
|
+
# @private
|
100
|
+
def inspect
|
101
|
+
"Stanza Error (#{@name}): #{self.text} [#{self.extras}]"
|
102
|
+
end
|
103
|
+
# @private
|
104
|
+
alias_method :to_s, :inspect
|
105
|
+
end # StanzaError
|
106
|
+
|
107
|
+
end # Blather
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Blather
|
2
|
+
|
3
|
+
# Stream Errors
|
4
|
+
# [RFC3920 Section 9.3](http://xmpp.org/rfcs/rfc3920.html#streams-error-rules)
|
5
|
+
#
|
6
|
+
# @handler :stream_error
|
7
|
+
class StreamError < BlatherError
|
8
|
+
STREAM_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-streams'
|
9
|
+
|
10
|
+
register :stream_error
|
11
|
+
|
12
|
+
attr_reader :text, :extras
|
13
|
+
|
14
|
+
# Factory method for instantiating the proper class for the error
|
15
|
+
#
|
16
|
+
# @param [Blather::XMPPNode] node the importable node
|
17
|
+
def self.import(node)
|
18
|
+
name = node.find_first('descendant::*[name()!="text"]', STREAM_ERR_NS).element_name
|
19
|
+
|
20
|
+
text = node.find_first 'descendant::*[name()="text"]', STREAM_ERR_NS
|
21
|
+
text = text.content if text
|
22
|
+
|
23
|
+
extras = node.find("descendant::*[namespace-uri()!='#{STREAM_ERR_NS}']").map { |n| n }
|
24
|
+
|
25
|
+
self.new name, text, extras
|
26
|
+
end
|
27
|
+
|
28
|
+
# Create a new Stream Error
|
29
|
+
# [RFC3920 Section 4.7.2](http://xmpp.org/rfcs/rfc3920.html#rfc.section.4.7.2)
|
30
|
+
#
|
31
|
+
# @param [String] name the error name
|
32
|
+
# @param [String, nil] text optional error text
|
33
|
+
# @param [Array<Blather::XMPPNode>] extras an array of extras to attach to the
|
34
|
+
# error
|
35
|
+
def initialize(name, text = nil, extras = [])
|
36
|
+
@name = name
|
37
|
+
@text = text
|
38
|
+
@extras = extras
|
39
|
+
end
|
40
|
+
|
41
|
+
# The error name
|
42
|
+
#
|
43
|
+
# @return [Symbol]
|
44
|
+
def name
|
45
|
+
@name.gsub('-','_').to_sym
|
46
|
+
end
|
47
|
+
|
48
|
+
# Creates an XML node from the error
|
49
|
+
#
|
50
|
+
# @return [Blather::XMPPNode]
|
51
|
+
def to_node
|
52
|
+
node = XMPPNode.new('stream:error')
|
53
|
+
|
54
|
+
node << (err = XMPPNode.new(@name, node.document))
|
55
|
+
err.namespace = 'urn:ietf:params:xml:ns:xmpp-streams'
|
56
|
+
|
57
|
+
if self.text
|
58
|
+
node << (text = XMPPNode.new('text', node.document))
|
59
|
+
text.namespace = 'urn:ietf:params:xml:ns:xmpp-streams'
|
60
|
+
text.content = self.text
|
61
|
+
end
|
62
|
+
|
63
|
+
self.extras.each { |extra| node << extra.dup }
|
64
|
+
node
|
65
|
+
end
|
66
|
+
|
67
|
+
# Convert the object to a proper node then convert it to a string
|
68
|
+
#
|
69
|
+
# @return [String]
|
70
|
+
def to_xml
|
71
|
+
to_node.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
# @private
|
75
|
+
def inspect
|
76
|
+
"Stream Error (#{@name}): #{self.text}" + (self.extras.empty? ? '' : " [#{self.extras}]")
|
77
|
+
end
|
78
|
+
# @private
|
79
|
+
alias_method :to_s, :inspect
|
80
|
+
end # StreamError
|
81
|
+
|
82
|
+
end # Blather
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Blather
|
2
|
+
# Main error class
|
3
|
+
# This starts the error hierarchy
|
4
|
+
#
|
5
|
+
# @handler :error
|
6
|
+
class BlatherError < StandardError
|
7
|
+
class_inheritable_array :handler_hierarchy
|
8
|
+
self.handler_hierarchy ||= []
|
9
|
+
|
10
|
+
# @private
|
11
|
+
@@handler_list = []
|
12
|
+
|
13
|
+
# Register the class's handler
|
14
|
+
#
|
15
|
+
# @param [Symbol] handler the handler name
|
16
|
+
def self.register(handler)
|
17
|
+
@@handler_list << handler
|
18
|
+
self.handler_hierarchy.unshift handler
|
19
|
+
end
|
20
|
+
|
21
|
+
# The list of registered handlers
|
22
|
+
#
|
23
|
+
# @return [Array<Symbol>] a list of currently registered handlers
|
24
|
+
def self.handler_list
|
25
|
+
@@handler_list
|
26
|
+
end
|
27
|
+
|
28
|
+
register :error
|
29
|
+
|
30
|
+
# @private
|
31
|
+
# HACK!! until I can refactor the entire Error object model
|
32
|
+
def id
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end # BlatherError
|
36
|
+
|
37
|
+
# Used in cases where a stanza only allows specific values for its attributes
|
38
|
+
# and an invalid value is attempted.
|
39
|
+
#
|
40
|
+
# @handler :argument_error
|
41
|
+
class ArgumentError < BlatherError
|
42
|
+
register :argument_error
|
43
|
+
end # ArgumentError
|
44
|
+
|
45
|
+
# The stream handler received a response it didn't know how to handle
|
46
|
+
#
|
47
|
+
# @handler :unknown_response_error
|
48
|
+
class UnknownResponse < BlatherError
|
49
|
+
register :unknown_response_error
|
50
|
+
attr_reader :node
|
51
|
+
|
52
|
+
def initialize(node)
|
53
|
+
@node = node
|
54
|
+
end
|
55
|
+
end # UnknownResponse
|
56
|
+
|
57
|
+
# Something bad happened while parsing the incoming stream
|
58
|
+
#
|
59
|
+
# @handler :parse_error
|
60
|
+
class ParseError < BlatherError
|
61
|
+
register :parse_error
|
62
|
+
attr_reader :message
|
63
|
+
|
64
|
+
def initialize(msg)
|
65
|
+
@message = msg.to_s
|
66
|
+
end
|
67
|
+
end # ParseError
|
68
|
+
|
69
|
+
end # Blather
|