sprsquish-blather 0.1 → 0.2.3
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/LICENSE +2 -0
- data/README.rdoc +100 -0
- data/Rakefile +110 -0
- data/examples/drb_client.rb +5 -0
- data/examples/echo.rb +18 -0
- data/ext/extconf.rb +65 -0
- data/ext/push_parser.c +231 -0
- data/lib/blather/client.rb +219 -44
- data/lib/blather/{core/sugar.rb → core_ext/active_support.rb} +25 -13
- data/lib/blather/core_ext/libxml.rb +28 -0
- data/lib/blather/errors/sasl_error.rb +87 -0
- data/lib/blather/errors/stanza_error.rb +262 -0
- data/lib/blather/errors/stream_error.rb +253 -0
- data/lib/blather/errors.rb +48 -0
- data/lib/blather/{core/jid.rb → jid.rb} +15 -26
- data/lib/blather/{core/roster.rb → roster.rb} +22 -0
- data/lib/blather/{core/roster_item.rb → roster_item.rb} +39 -8
- data/lib/blather/stanza/iq/disco.rb +11 -0
- data/lib/blather/stanza/iq/discos/disco_info.rb +86 -0
- data/lib/blather/stanza/iq/discos/disco_items.rb +61 -0
- data/lib/blather/stanza/iq/query.rb +51 -0
- data/lib/blather/stanza/iq/roster.rb +90 -0
- data/lib/blather/stanza/iq.rb +38 -0
- data/lib/blather/stanza/message.rb +58 -0
- data/lib/blather/stanza/presence/status.rb +78 -0
- data/lib/blather/stanza/presence/subscription.rb +72 -0
- data/lib/blather/stanza/presence.rb +45 -0
- data/lib/blather/stanza.rb +101 -0
- data/lib/blather/stream/client.rb +26 -0
- data/lib/blather/stream/component.rb +34 -0
- data/lib/blather/stream/parser.rb +70 -0
- data/lib/blather/stream/resource.rb +48 -0
- data/lib/blather/stream/sasl.rb +173 -0
- data/lib/blather/stream/session.rb +36 -0
- data/lib/blather/stream/stream_handler.rb +39 -0
- data/lib/blather/stream/tls.rb +33 -0
- data/lib/blather/stream.rb +249 -0
- data/lib/blather/xmpp_node.rb +199 -0
- data/lib/blather.rb +40 -41
- data/spec/blather/core_ext/libxml_spec.rb +58 -0
- data/spec/blather/errors/sasl_error_spec.rb +56 -0
- data/spec/blather/errors/stanza_error_spec.rb +148 -0
- data/spec/blather/errors/stream_error_spec.rb +114 -0
- data/spec/blather/errors_spec.rb +40 -0
- data/spec/blather/{core/jid_spec.rb → jid_spec.rb} +9 -1
- data/spec/blather/{core/roster_item_spec.rb → roster_item_spec.rb} +6 -1
- data/spec/blather/{core/roster_spec.rb → roster_spec.rb} +16 -6
- data/spec/blather/stanza/iq/discos/disco_info_spec.rb +207 -0
- data/spec/blather/stanza/iq/discos/disco_items_spec.rb +136 -0
- data/spec/blather/stanza/iq/query_spec.rb +34 -0
- data/spec/blather/stanza/iq/roster_spec.rb +123 -0
- data/spec/blather/stanza/iq_spec.rb +40 -0
- data/spec/blather/stanza/message_spec.rb +52 -0
- data/spec/blather/stanza/presence/status_spec.rb +102 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +85 -0
- data/spec/blather/stanza/presence_spec.rb +53 -0
- data/spec/blather/{core/stanza_spec.rb → stanza_spec.rb} +14 -2
- data/spec/blather/stream/client_spec.rb +787 -0
- data/spec/blather/stream/component_spec.rb +86 -0
- data/spec/blather/{core/xmpp_node_spec.rb → xmpp_node_spec.rb} +76 -23
- data/spec/build_safe.rb +20 -0
- data/spec/spec_helper.rb +7 -17
- metadata +79 -59
- data/CHANGELOG +0 -1
- data/blather.gemspec +0 -73
- data/lib/blather/callback.rb +0 -24
- data/lib/blather/core/errors.rb +0 -24
- data/lib/blather/core/stanza.rb +0 -90
- data/lib/blather/core/stream.rb +0 -179
- data/lib/blather/core/xmpp_node.rb +0 -95
- data/lib/blather/extensions/last_activity.rb +0 -57
- data/lib/blather/extensions/version.rb +0 -85
- data/spec/blather/core/stream_spec.rb +0 -263
data/lib/blather/client.rb
CHANGED
@@ -1,81 +1,256 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__),
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. blather])
|
2
2
|
|
3
|
-
module Blather
|
4
|
-
|
5
|
-
class Client
|
6
|
-
@@callbacks = {}
|
7
|
-
@@status = nil
|
3
|
+
module Blather #:nodoc:
|
8
4
|
|
5
|
+
class Client #:nodoc:
|
9
6
|
attr_accessor :jid,
|
10
7
|
:roster
|
11
8
|
|
12
|
-
def
|
13
|
-
@
|
9
|
+
def initialize
|
10
|
+
@state = :initializing
|
11
|
+
|
12
|
+
@status = Stanza::Presence::Status.new
|
13
|
+
@handlers = {}
|
14
|
+
@tmp_handlers = {}
|
15
|
+
@roster = Roster.new self
|
16
|
+
|
17
|
+
setup_initial_handlers
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup(jid, password, host = nil, port = 5222)
|
21
|
+
@setup = [JID.new(jid), password, host, port]
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup?
|
26
|
+
@setup.is_a?(Array) && !@setup.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
raise 'Not setup!' unless @setup.is_a?(Array)
|
31
|
+
trap(:INT) { EM.stop }
|
32
|
+
EM.run {
|
33
|
+
klass = @setup[2].node ? Blather::Stream::Client : Blather::Stream::Component
|
34
|
+
klass.start Blather.client, *@setup
|
35
|
+
}
|
36
|
+
|
37
|
+
def temporary_handler(id, &handler)
|
38
|
+
@tmp_handlers[id] = handler
|
39
|
+
end
|
40
|
+
|
41
|
+
def register_handler(type, *guards, &handler)
|
42
|
+
@handlers[type] ||= []
|
43
|
+
@handlers[type] << [guards, handler]
|
14
44
|
end
|
15
45
|
|
16
46
|
def status
|
17
|
-
|
47
|
+
@status.state
|
18
48
|
end
|
19
49
|
|
20
|
-
def
|
21
|
-
|
50
|
+
def status=(state)
|
51
|
+
state, msg, to = state
|
52
|
+
|
53
|
+
status = Stanza::Presence::Status.new state, msg
|
22
54
|
status.to = to
|
23
|
-
|
55
|
+
@statustatus unless to
|
56
|
+
|
57
|
+
write status
|
58
|
+
end
|
24
59
|
|
25
|
-
|
60
|
+
def write(stanza)
|
61
|
+
stanza.from ||= jid if stanza.respond_to?(:from)
|
62
|
+
@stream.send(stanza) if @stream
|
26
63
|
end
|
27
64
|
|
28
65
|
def stream_started(stream)
|
29
66
|
@stream = stream
|
30
|
-
|
67
|
+
|
68
|
+
#retreive roster
|
69
|
+
if @stream.is_a?(Stream::Component)
|
70
|
+
@state = :ready
|
71
|
+
call_handler_for :ready, nil
|
72
|
+
else
|
73
|
+
write Stanza::Iq::Roster.new
|
74
|
+
end
|
31
75
|
end
|
32
76
|
|
33
|
-
def
|
34
|
-
|
77
|
+
def stop
|
78
|
+
@stream.close_connection_after_writing
|
35
79
|
end
|
36
80
|
|
37
|
-
|
38
|
-
|
39
|
-
send_data(ErrorStanza.new_from(iq, 'service-unavailable', 'cancel').reply!) if [:set, :get].include?(iq.type)
|
81
|
+
def stopped
|
82
|
+
EM.stop
|
40
83
|
end
|
41
84
|
|
42
|
-
def
|
43
|
-
if
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
85
|
+
def call(stanza)
|
86
|
+
if handler = @tmp_handlers.delete(stanza.id)
|
87
|
+
handler.call stanza
|
88
|
+
else
|
89
|
+
stanza.handler_heirarchy.each do |type|
|
90
|
+
break if call_handler_for(type, stanza) && (stanza.is_a?(BlatherError) || stanza.type == :iq)
|
91
|
+
end
|
49
92
|
end
|
50
93
|
end
|
51
94
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
95
|
+
def call_handler_for(type, stanza)
|
96
|
+
if @handlers[type]
|
97
|
+
@handlers[type].find { |guards, handler| handler.call(stanza) unless guarded?(guards, stanza) }
|
98
|
+
true
|
99
|
+
end
|
56
100
|
end
|
57
101
|
|
58
|
-
|
59
|
-
|
60
|
-
|
102
|
+
protected
|
103
|
+
def setup_initial_handlers
|
104
|
+
register_handler :error do |err|
|
105
|
+
raise err
|
106
|
+
end
|
61
107
|
|
62
|
-
|
63
|
-
|
64
|
-
|
108
|
+
register_handler :iq do |iq|
|
109
|
+
write(StanzaError::ServiceUnavailable.new(iq, :cancel).to_node) if [:set, :get].include?(iq.type)
|
110
|
+
end
|
65
111
|
|
66
|
-
|
67
|
-
|
68
|
-
|
112
|
+
register_handler :status do |status|
|
113
|
+
roster[status.from].status = status if roster[status.from]
|
114
|
+
end
|
69
115
|
|
70
|
-
|
71
|
-
|
72
|
-
|
116
|
+
register_handler :roster do |node|
|
117
|
+
roster.process node
|
118
|
+
if @state == :initializing
|
119
|
+
@state = :ready
|
120
|
+
write @status
|
121
|
+
call_handler_for :ready, nil
|
122
|
+
end
|
123
|
+
end
|
73
124
|
end
|
74
125
|
|
75
|
-
|
76
|
-
|
126
|
+
##
|
127
|
+
# If any of the guards returns FALSE this returns true
|
128
|
+
def guarded?(guards, stanza)
|
129
|
+
guards.find do |guard|
|
130
|
+
case guard
|
131
|
+
when Symbol
|
132
|
+
!stanza.__send__(guard)
|
133
|
+
when Array
|
134
|
+
# return FALSE if any item is TRUE
|
135
|
+
!guard.detect { |condition| !guarded?([condition], stanza) }
|
136
|
+
when Hash
|
137
|
+
# return FALSE unless any inequality is found
|
138
|
+
guard.find do |method, value|
|
139
|
+
if value.is_a?(Regexp)
|
140
|
+
!stanza.__send__(method).to_s.match(value)
|
141
|
+
else
|
142
|
+
stanza.__send__(method) != value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
when Proc
|
146
|
+
!guard.call(stanza)
|
147
|
+
else
|
148
|
+
raise "Bad guard: #{guard.inspect}"
|
149
|
+
end
|
150
|
+
end
|
77
151
|
end
|
78
152
|
|
79
153
|
end #Client
|
80
154
|
|
155
|
+
def client
|
156
|
+
@client ||= Client.new
|
157
|
+
end
|
158
|
+
module_function :client
|
159
|
+
end #Blather
|
160
|
+
|
161
|
+
##
|
162
|
+
# Prepare server settings
|
163
|
+
# setup_client [node@domain/resource], [password], [host], [port]
|
164
|
+
# host and port are optional defaulting to the domain in the JID and 5222 respectively
|
165
|
+
def setup_client(jid, password, host = nil, port = 5222)
|
166
|
+
at_exit { Blather.client.setup_client(jid, password, host, port).run }
|
167
|
+
end
|
168
|
+
|
169
|
+
def setup_component(jid, secret, host, port)
|
170
|
+
at_exit { Blather.client.setup_component(jid, secret, host, port).run }
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# Shutdown the connection.
|
175
|
+
# Flushes the write buffer then stops EventMachine
|
176
|
+
def shutdown
|
177
|
+
Blather.client.stop
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Set handler for a stanza type
|
182
|
+
def handle(stanza_type, *guards, &block)
|
183
|
+
Blather.client.register_handler stanza_type, *guards, &block
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# Wrapper for "handle :ready" (just a bit of syntactic sugar)
|
188
|
+
def when_ready(&block)
|
189
|
+
handle :ready, &block
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Set current status
|
194
|
+
def status(state = nil, msg = nil)
|
195
|
+
Blather.client.status = state, msg
|
196
|
+
end
|
197
|
+
|
198
|
+
##
|
199
|
+
# Direct access to the roster
|
200
|
+
def roster
|
201
|
+
Blather.client.roster
|
202
|
+
end
|
203
|
+
|
204
|
+
##
|
205
|
+
# Write data to the stream
|
206
|
+
# Anything that resonds to #to_s can be paseed to the stream
|
207
|
+
def write(stanza)
|
208
|
+
Blather.client.write(stanza)
|
209
|
+
end
|
210
|
+
|
211
|
+
##
|
212
|
+
# Helper method to make sending basic messages easier
|
213
|
+
# say [jid], [msg]
|
214
|
+
def say(to, msg)
|
215
|
+
Blather.client.write Blather::Stanza::Message.new(to, msg)
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# Wrapper to grab the current JID
|
220
|
+
def jid
|
221
|
+
Blather.client.jid
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
#
|
226
|
+
def discover(what, who, where, &callback)
|
227
|
+
stanza = Blather::Stanza.class_from_registration(:query, "http://jabber.org/protocol/disco##{what}").new
|
228
|
+
stanza.to = who
|
229
|
+
stanza.node = where
|
230
|
+
|
231
|
+
Blather.client.temporary_handler stanza.id, &callback
|
232
|
+
write stanza
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# Checks to see if the method is part of the handlers list.
|
237
|
+
# If so it creates a handler, otherwise it'll pass it back
|
238
|
+
# to Ruby's method_missing handler
|
239
|
+
def method_missing(method, *args, &block)
|
240
|
+
if Blather::Stanza.handler_list.include?(method)
|
241
|
+
handle method, *args, &block
|
242
|
+
else
|
243
|
+
super
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
at_exit do
|
249
|
+
unless Blather.client.setup?
|
250
|
+
if ARGV.length < 2
|
251
|
+
puts "Run with #{$0} user@server/resource password [host] [port]"
|
252
|
+
else
|
253
|
+
Blather.client.setup(*ARGV).run
|
254
|
+
end
|
255
|
+
end
|
81
256
|
end
|
@@ -1,16 +1,9 @@
|
|
1
|
-
|
2
|
-
module XML
|
3
|
-
|
4
|
-
class Attributes
|
5
|
-
def remove(name)
|
6
|
-
name = name.to_s
|
7
|
-
self.each { |a| a.remove! or break if a.name == name }
|
8
|
-
end
|
9
|
-
end #Attributes
|
10
|
-
|
11
|
-
end #XML
|
12
|
-
end #LibXML
|
1
|
+
# Thanks to Rails ActiveSupport for everything in this file
|
13
2
|
|
3
|
+
# Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of
|
4
|
+
# their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
|
5
|
+
# to, for example, an array without those additions being shared with either their parent, siblings, or
|
6
|
+
# children, which is unlike the regular class-level attributes that are shared across the entire hierarchy.
|
14
7
|
class Class # :nodoc:
|
15
8
|
def class_inheritable_reader(*syms)
|
16
9
|
syms.each do |sym|
|
@@ -123,20 +116,38 @@ class Class # :nodoc:
|
|
123
116
|
alias inherited inherited_with_inheritable_attributes
|
124
117
|
end #Class
|
125
118
|
|
126
|
-
class Object
|
119
|
+
class Object # :nodoc:
|
127
120
|
def duplicable?; true; end
|
121
|
+
def blank?; respond_to?(:empty?) ? empty? : !self; end
|
122
|
+
def present?; !blank?; end
|
123
|
+
end
|
124
|
+
|
125
|
+
class Array #:nodoc:
|
126
|
+
alias_method :blank?, :empty?
|
127
|
+
def extract_options!; last.is_a?(::Hash) ? pop : {}; end
|
128
|
+
end
|
129
|
+
|
130
|
+
class Hash #:nodoc:
|
131
|
+
alias_method :blank?, :empty?
|
132
|
+
end
|
133
|
+
|
134
|
+
class String #:nodoc:
|
135
|
+
def blank?; self !~ /\S/; end
|
128
136
|
end
|
129
137
|
|
130
138
|
class NilClass #:nodoc:
|
131
139
|
def duplicable?; false; end
|
140
|
+
def blank?; true; end
|
132
141
|
end
|
133
142
|
|
134
143
|
class FalseClass #:nodoc:
|
135
144
|
def duplicable?; false; end
|
145
|
+
def blank?; true; end
|
136
146
|
end
|
137
147
|
|
138
148
|
class TrueClass #:nodoc:
|
139
149
|
def duplicable?; false; end
|
150
|
+
def blank?; false; end
|
140
151
|
end
|
141
152
|
|
142
153
|
class Symbol #:nodoc:
|
@@ -145,4 +156,5 @@ end
|
|
145
156
|
|
146
157
|
class Numeric #:nodoc:
|
147
158
|
def duplicable?; false; end
|
159
|
+
def blank?; false; end
|
148
160
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module LibXML # :nodoc:
|
2
|
+
module XML # :nodoc:
|
3
|
+
|
4
|
+
class Node
|
5
|
+
alias_method :element_name, :name
|
6
|
+
alias_method :element_name=, :name=
|
7
|
+
end
|
8
|
+
|
9
|
+
class Attributes
|
10
|
+
# Helper method for removing attributes
|
11
|
+
def remove(name)
|
12
|
+
attribute = get_attribute(name.to_s)
|
13
|
+
attribute.remove! if attribute
|
14
|
+
end
|
15
|
+
|
16
|
+
alias_method :old_hash_set, :[]= # :nodoc:
|
17
|
+
def []=(name, val)
|
18
|
+
val.nil? ? remove(name.to_s) : old_hash_set(name.to_s, val.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method :old_hash_get, :[] # :nodoc:
|
22
|
+
def [](name)
|
23
|
+
old_hash_get name.to_s
|
24
|
+
end
|
25
|
+
end #Attributes
|
26
|
+
|
27
|
+
end #XML
|
28
|
+
end #LibXML
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Blather
|
2
|
+
|
3
|
+
class SASLError < BlatherError
|
4
|
+
class_inheritable_accessor :err_name
|
5
|
+
@@registrations = {}
|
6
|
+
|
7
|
+
register :sasl_error
|
8
|
+
|
9
|
+
##
|
10
|
+
# Register the handler and type to simplify importing
|
11
|
+
def self.register(handler, err_name)
|
12
|
+
super handler
|
13
|
+
self.err_name = err_name
|
14
|
+
@@registrations[err_name] = self
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Retreive an error class from a given name
|
19
|
+
def self.class_from_registration(err_name)
|
20
|
+
@@registrations[err_name.to_s] || self
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Factory to create the proper error object from an error node
|
25
|
+
def self.import(node)
|
26
|
+
err_name = node.children.first.element_name
|
27
|
+
class_from_registration(err_name).new
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# XMPP defined error name
|
32
|
+
def err_name
|
33
|
+
self.class.err_name
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# The receiving entity acknowledges an <abort/> element sent by the initiating entity; sent in reply to the <abort/> element.
|
38
|
+
class Aborted < SASLError
|
39
|
+
register :sasl_aborted_error, 'aborted'
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# The data provided by the initiating entity could not be processed because the [BASE64] encoding is incorrect
|
44
|
+
# (e.g., because the encoding does not adhere to the definition in Section 3 of [BASE64]); sent in reply to a <response/>
|
45
|
+
# element or an <auth/> element with initial response data.
|
46
|
+
class IncorrectEncoding < SASLError
|
47
|
+
register :sasl_incorrect_encoding_error, 'incorrect-encoding'
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# The authzid provided by the initiating entity is invalid, either because it is incorrectly formatted or because the
|
52
|
+
# initiating entity does not have permissions to authorize that ID; sent in reply to a <response/> element or an <auth/>
|
53
|
+
# element with initial response data.
|
54
|
+
class InvalidAuthzid < SASLError
|
55
|
+
register :sasl_invalid_authzid_error, 'invalid-authzid'
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# The initiating entity did not provide a mechanism or requested a mechanism that is not supported by the receiving entity;
|
60
|
+
# sent in reply to an <auth/> element.
|
61
|
+
class InvalidMechanism < SASLError
|
62
|
+
register :sasl_invalid_mechanism_error, 'invalid-mechanism'
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# The mechanism requested by the initiating entity is weaker than server policy permits for that initiating entity; sent in
|
67
|
+
# reply to a <response/> element or an <auth/> element with initial response data.
|
68
|
+
class MechanismTooWeak < SASLError
|
69
|
+
register :sasl_mechanism_too_weak_error, 'mechanism-too-weak'
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# The authentication failed because the initiating entity did not provide valid credentials (this includes but is not limited
|
74
|
+
# to the case of an unknown username); sent in reply to a <response/> element or an <auth/> element with initial response data.
|
75
|
+
class NotAuthorized < SASLError
|
76
|
+
register :sasl_not_authorized_error, 'not-authorized'
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# The authentication failed because of a temporary error condition within the receiving entity; sent in reply to an <auth/>
|
81
|
+
# element or <response/> element.
|
82
|
+
class TemporaryAuthFailure < SASLError
|
83
|
+
register :sasl_temporary_auth_failure_error, 'temporary-auth-failure'
|
84
|
+
end
|
85
|
+
end #SASLError
|
86
|
+
|
87
|
+
end #Blather
|