blather 0.2.3 → 0.3.0
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/README.rdoc +7 -4
- data/Rakefile +3 -1
- data/examples/print_heirarchy.rb +76 -0
- data/lib/blather.rb +3 -3
- data/lib/blather/client.rb +4 -247
- data/lib/blather/client/client.rb +168 -0
- data/lib/blather/client/dsl.rb +99 -0
- data/lib/blather/errors.rb +5 -0
- data/lib/blather/errors/sasl_error.rb +6 -70
- data/lib/blather/errors/stanza_error.rb +12 -176
- data/lib/blather/errors/stream_error.rb +8 -186
- data/lib/blather/stanza.rb +2 -3
- data/lib/blather/stanza/{iq/disco.rb → disco.rb} +1 -3
- data/lib/blather/stanza/{iq/discos → disco}/disco_info.rb +3 -5
- data/lib/blather/stanza/{iq/discos → disco}/disco_items.rb +0 -2
- data/lib/blather/stanza/iq/query.rb +1 -1
- data/lib/blather/stanza/iq/roster.rb +2 -2
- data/lib/blather/stanza/pubsub/subscriber.rb +64 -0
- data/lib/blather/stream.rb +13 -7
- data/lib/blather/stream/component.rb +1 -1
- data/lib/blather/stream/parser.rb +11 -4
- data/lib/blather/stream/resource.rb +1 -1
- data/lib/blather/stream/sasl.rb +15 -9
- data/lib/blather/xmpp_node.rb +10 -4
- data/spec/blather/client/client_spec.rb +4 -0
- data/spec/blather/client/dsl_spec.rb +4 -0
- data/spec/blather/client_spec.rb +0 -0
- data/spec/blather/errors/sasl_error_spec.rb +2 -25
- data/spec/blather/errors/stanza_error_spec.rb +7 -18
- data/spec/blather/errors/stream_error_spec.rb +4 -15
- data/spec/blather/stanza/{iq/discos → discos}/disco_info_spec.rb +12 -12
- data/spec/blather/stanza/{iq/discos → discos}/disco_items_spec.rb +1 -1
- data/spec/blather/stanza/iq/query_spec.rb +7 -0
- data/spec/blather/stanza/iq/roster_spec.rb +21 -21
- data/spec/blather/stanza/pubsub/subscriber_spec.rb +70 -0
- data/spec/blather/stanza_spec.rb +1 -7
- data/spec/blather/stream/client_spec.rb +36 -7
- data/spec/spec_helper.rb +1 -1
- metadata +16 -18
- data/ext/Makefile +0 -149
- data/ext/mkmf.log +0 -30
- data/ext/push_parser.bundle +0 -0
- data/ext/push_parser.o +0 -0
- data/lib/autotest/discover.rb +0 -1
- data/lib/autotest/spec.rb +0 -60
- data/spec/blather/stanza/pubsub/event_spec.rb +0 -13
- data/spec/build_safe.rb +0 -20
data/lib/blather/stanza.rb
CHANGED
@@ -93,9 +93,8 @@ module Blather
|
|
93
93
|
# Transform the stanza into a stanza error
|
94
94
|
# <tt>err_name_or_class</tt> can be the name of the error or the error class to use
|
95
95
|
# <tt>type</tt>, <tt>text</tt>, <tt>extras</tt> are the same as for StanzaError#new
|
96
|
-
def as_error(
|
97
|
-
|
98
|
-
klass.new self, type, text, extras
|
96
|
+
def as_error(name, type, text = nil, extras = [])
|
97
|
+
StanzaError.new self, name, type, text, extras
|
99
98
|
end
|
100
99
|
end
|
101
100
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Blather
|
2
2
|
class Stanza
|
3
|
-
class Iq
|
4
3
|
|
5
4
|
##
|
6
5
|
# DiscoInfo object ()
|
@@ -8,9 +7,11 @@ class Iq
|
|
8
7
|
class DiscoInfo < Disco
|
9
8
|
register :disco_info, nil, 'http://jabber.org/protocol/disco#info'
|
10
9
|
|
11
|
-
def initialize(type = nil,
|
10
|
+
def initialize(type = nil, node = nil, identities = [], features = [])
|
12
11
|
super type
|
13
12
|
|
13
|
+
self.node = node
|
14
|
+
|
14
15
|
[identities].flatten.each do |id|
|
15
16
|
query << (id.is_a?(Identity) ? id : Identity.new(id[:name], id[:type], id[:category]))
|
16
17
|
end
|
@@ -18,8 +19,6 @@ class Iq
|
|
18
19
|
[features].flatten.each do |feature|
|
19
20
|
query << (feature.is_a?(Feature) ? feature : Feature.new(feature))
|
20
21
|
end
|
21
|
-
|
22
|
-
self.node = node
|
23
22
|
end
|
24
23
|
|
25
24
|
##
|
@@ -81,6 +80,5 @@ class Iq
|
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
84
|
-
end #Iq
|
85
83
|
end #Stanza
|
86
84
|
end #Blather
|
@@ -27,8 +27,8 @@ class Iq
|
|
27
27
|
##
|
28
28
|
# Roster items
|
29
29
|
def items
|
30
|
-
items = query.find('item')
|
31
|
-
items = query.find('query_ns:item', :query_ns => self.class.ns) if items.empty?
|
30
|
+
items = query.find('//item', self.class.ns)
|
31
|
+
items = query.find('//query_ns:item', :query_ns => self.class.ns) if items.empty?
|
32
32
|
items.map { |i| RosterItem.new(i) }
|
33
33
|
end
|
34
34
|
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
class PubSub
|
4
|
+
|
5
|
+
module Subscriber
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
extend ClassMethods
|
9
|
+
include InstanceMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def subscribe(host, node, jid)
|
15
|
+
stanza = self.new(:set, host)
|
16
|
+
subscription = XMPPNode.new 'subscription'
|
17
|
+
subscription.attributes[:node] = node
|
18
|
+
subscription.attributes[:jid] = JID.new(jid).stripped
|
19
|
+
stanza.pubsub << subscription
|
20
|
+
stanza
|
21
|
+
end
|
22
|
+
|
23
|
+
def unsubscribe(host, node, jid, subid = nil)
|
24
|
+
stanza = self.new(:set, host)
|
25
|
+
unsubscription = XMPPNode.new 'unsubscribe'
|
26
|
+
unsubscription.attributes[:node] = node
|
27
|
+
unsubscription.attributes[:jid] = JID.new(jid).stripped
|
28
|
+
unsubscription.attributes[:subid] = subid
|
29
|
+
stanza.pubsub << unsubscription
|
30
|
+
stanza
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
def subscription
|
36
|
+
if sub = subscription?
|
37
|
+
{ :node => sub.attributes[:node],
|
38
|
+
:jid => JID.new(sub.attributes[:jid]),
|
39
|
+
:subid => sub.attributes[:subid],
|
40
|
+
:subscription => sub.attributes[:subscription] }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def subscription?
|
45
|
+
find_first('//pubsub_ns:pubsub/pubsub_ns:subscription', :pubsub_ns => self.ns)
|
46
|
+
end
|
47
|
+
|
48
|
+
def unsubscribe
|
49
|
+
if sub = unsubscribe?
|
50
|
+
{ :node => sub.attributes[:node],
|
51
|
+
:jid => JID.new(sub.attributes[:jid]) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def unsubscribe?
|
56
|
+
find_first('//pubsub_ns:pubsub/pubsub_ns:unsubscribe', :pubsub_ns => self.ns)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end #Subscriber
|
61
|
+
|
62
|
+
end #PubSub
|
63
|
+
end #Stanza
|
64
|
+
end #Blather
|
data/lib/blather/stream.rb
CHANGED
@@ -97,23 +97,23 @@ module Blather
|
|
97
97
|
LOG.debug "RECEIVING (#{node.element_name}) #{node}"
|
98
98
|
@node = node
|
99
99
|
|
100
|
+
if @node.find_first('//stream:error', :stream => 'http://etherx.jabber.org/streams')
|
101
|
+
handle_stream_error
|
102
|
+
return
|
103
|
+
end
|
104
|
+
|
100
105
|
case @node.element_name
|
101
|
-
when 'stream
|
106
|
+
when 'stream'
|
102
107
|
@state = :ready if @state == :stopped
|
103
108
|
|
104
109
|
when 'stream:end'
|
105
110
|
stop
|
106
111
|
|
107
|
-
when '
|
112
|
+
when 'features'
|
108
113
|
@features = @node.children
|
109
114
|
@state = :features
|
110
115
|
dispatch
|
111
116
|
|
112
|
-
when 'stream:error'
|
113
|
-
@error = StreamError.import @node
|
114
|
-
stop
|
115
|
-
@state = :error
|
116
|
-
|
117
117
|
else
|
118
118
|
dispatch
|
119
119
|
|
@@ -165,6 +165,12 @@ module Blather
|
|
165
165
|
@client.call @node.to_stanza
|
166
166
|
end
|
167
167
|
|
168
|
+
def handle_stream_error
|
169
|
+
@error = StreamError.import @node
|
170
|
+
stop
|
171
|
+
@state = :error
|
172
|
+
end
|
173
|
+
|
168
174
|
##
|
169
175
|
# Called when @state == :features
|
170
176
|
# Runs through the list of features starting each one in turn
|
@@ -11,7 +11,7 @@ class Stream
|
|
11
11
|
super
|
12
12
|
end
|
13
13
|
|
14
|
-
if node.
|
14
|
+
if node.namespaces.find_by_href('http://etherx.jabber.org/streams') && node.find_first('/stream:stream[not(stream:error)]')
|
15
15
|
send("<handshake>#{Digest::SHA1.hexdigest(@node['id']+@pass)}</handshake>")
|
16
16
|
end
|
17
17
|
end
|
@@ -19,21 +19,28 @@ class Stream # :nodoc:
|
|
19
19
|
|
20
20
|
def receive_data(string)
|
21
21
|
LOG.debug "PARSING: (#{string})" if @@debug
|
22
|
+
@stream_error = string =~ /stream:error/
|
22
23
|
@parser.receive string
|
23
24
|
end
|
24
25
|
|
25
26
|
def on_start_element_ns(elem, attrs, prefix, uri, namespaces)
|
26
27
|
LOG.debug "START ELEM: (#{{:elem => elem, :attrs => attrs, :prefix => prefix, :uri => uri, :ns => namespaces}.inspect})" if @@debug
|
27
|
-
|
28
|
+
|
28
29
|
e = XMPPNode.new elem
|
29
|
-
XML::Namespace.new(e, prefix, uri)
|
30
30
|
attrs.each { |k,v| e.attributes[k] = v if k }
|
31
31
|
|
32
|
-
if
|
32
|
+
if @current && (ns = @current.namespaces.find_by_href(uri))
|
33
|
+
e.namespace = ns
|
34
|
+
else
|
35
|
+
e.namespace = {prefix => uri}
|
36
|
+
end
|
37
|
+
|
38
|
+
if elem == 'stream' && !@stream_error
|
39
|
+
XML::Document.new.root = e
|
33
40
|
@receiver.receive e
|
34
41
|
|
35
42
|
elsif !@receiver.stopped?
|
36
|
-
@current << e
|
43
|
+
@current << e if @current
|
37
44
|
@current = e
|
38
45
|
|
39
46
|
end
|
@@ -32,7 +32,7 @@ class Stream # :nodoc:
|
|
32
32
|
LOG.debug "RESOURE NODE #{@node}"
|
33
33
|
# ensure this is a response to our original request
|
34
34
|
if @id == @node['id']
|
35
|
-
@jid = JID.new @node.find_first('bind/jid').content
|
35
|
+
@jid = JID.new @node.find_first('//bind_ns:bind/bind_ns:jid', :bind_ns => 'urn:ietf:params:xml:ns:xmpp-bind').content
|
36
36
|
success @jid
|
37
37
|
end
|
38
38
|
end
|
data/lib/blather/stream/sasl.rb
CHANGED
@@ -18,19 +18,25 @@ class Stream # :nodoc:
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def set_mechanism
|
21
|
-
mod =
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
mod = if @jid.node == '' && @mechanisms.find { |m| m.content == 'ANONYMOUS' }
|
22
|
+
Anonymous
|
23
|
+
else
|
24
|
+
case (mechanism = @mechanisms[@mechanism_idx].content)
|
25
|
+
when 'DIGEST-MD5' then DigestMD5
|
26
|
+
when 'PLAIN' then Plain
|
27
|
+
when 'ANONYMOUS' then Anonymous
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if mod
|
32
|
+
extend mod
|
33
|
+
true
|
25
34
|
else
|
26
35
|
# Send a failure node and kill the stream
|
27
36
|
@stream.send "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><invalid-mechanism/></failure>"
|
28
37
|
@failure.call UnknownMechanism.new("Unknown SASL mechanism (#{mechanism})")
|
29
|
-
|
38
|
+
false
|
30
39
|
end
|
31
|
-
|
32
|
-
extend mod
|
33
|
-
true
|
34
40
|
end
|
35
41
|
|
36
42
|
##
|
@@ -164,7 +170,7 @@ class Stream # :nodoc:
|
|
164
170
|
|
165
171
|
module Anonymous # :nodoc:
|
166
172
|
def authenticate
|
167
|
-
@stream.send auth_node('ANONYMOUS'
|
173
|
+
@stream.send auth_node('ANONYMOUS')
|
168
174
|
end
|
169
175
|
end #Anonymous
|
170
176
|
end #SASL
|
data/lib/blather/xmpp_node.rb
CHANGED
@@ -126,10 +126,16 @@ module Blather
|
|
126
126
|
self.class.import self
|
127
127
|
end
|
128
128
|
|
129
|
-
def namespace=(
|
130
|
-
|
131
|
-
|
132
|
-
|
129
|
+
def namespace=(namespaces)
|
130
|
+
case namespaces
|
131
|
+
when XML::Namespace
|
132
|
+
self.namespaces.namespace = namespaces
|
133
|
+
when String
|
134
|
+
self.namespaces.namespace = XML::Namespace.new(self, nil, namespaces)
|
135
|
+
when Hash
|
136
|
+
namespaces.each do |p, n|
|
137
|
+
self.namespaces.namespace = XML::Namespace.new(self, p, n)
|
138
|
+
end
|
133
139
|
end
|
134
140
|
end
|
135
141
|
|
File without changes
|
@@ -15,23 +15,6 @@ describe 'Blather::SASLError' do
|
|
15
15
|
e.must_be_kind_of SASLError
|
16
16
|
end
|
17
17
|
|
18
|
-
it 'knows what class to instantiate' do
|
19
|
-
e = SASLError.import sasl_error_node
|
20
|
-
e.must_be_instance_of SASLError::Aborted
|
21
|
-
end
|
22
|
-
|
23
|
-
describe 'when instantiated' do
|
24
|
-
before do
|
25
|
-
@err_name = 'mechanism-too-weak'
|
26
|
-
@err = SASLError.import sasl_error_node(@err_name)
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'provides a err_name attribute' do
|
30
|
-
@err.must_respond_to :err_name
|
31
|
-
@err.err_name.must_equal @err_name
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
18
|
describe 'each XMPP SASL error type' do
|
36
19
|
%w[ aborted
|
37
20
|
incorrect-encoding
|
@@ -41,15 +24,9 @@ describe 'Blather::SASLError' do
|
|
41
24
|
not-authorized
|
42
25
|
temporary-auth-failure
|
43
26
|
].each do |error_type|
|
44
|
-
it "
|
45
|
-
e = SASLError.import sasl_error_node(error_type)
|
46
|
-
klass = error_type.gsub(/^\w/) { |v| v.upcase }.gsub(/\-(\w)/) { |v| v.delete('-').upcase }
|
47
|
-
e.must_be_instance_of eval("SASLError::#{klass}")
|
48
|
-
end
|
49
|
-
|
50
|
-
it "registers #{error_type} in the handler heirarchy" do
|
27
|
+
it "handles the name for #{error_type}" do
|
51
28
|
e = SASLError.import sasl_error_node(error_type)
|
52
|
-
e.
|
29
|
+
e.name.must_equal error_type.gsub('-','_').to_sym
|
53
30
|
end
|
54
31
|
end
|
55
32
|
end
|
@@ -34,19 +34,14 @@ describe 'Blather::StanzaError' do
|
|
34
34
|
e.must_be_kind_of StanzaError
|
35
35
|
end
|
36
36
|
|
37
|
-
it 'knows what class to instantiate' do
|
38
|
-
e = StanzaError.import stanza_error_node
|
39
|
-
e.must_be_instance_of StanzaError::InternalServerError
|
40
|
-
end
|
41
|
-
|
42
37
|
describe 'valid types' do
|
43
38
|
before { @original = Stanza::Message.new 'error@jabber.local', 'test message', :error }
|
44
39
|
|
45
40
|
it 'ensures type is one of Stanza::Message::VALID_TYPES' do
|
46
|
-
lambda { StanzaError.new @original, :invalid_type_name }.must_raise(Blather::ArgumentError)
|
41
|
+
lambda { StanzaError.new @original, :gone, :invalid_type_name }.must_raise(Blather::ArgumentError)
|
47
42
|
|
48
43
|
StanzaError::VALID_TYPES.each do |valid_type|
|
49
|
-
msg = StanzaError.new @original, valid_type
|
44
|
+
msg = StanzaError.new @original, :gone, valid_type
|
50
45
|
msg.type.must_equal valid_type
|
51
46
|
end
|
52
47
|
end
|
@@ -65,9 +60,9 @@ describe 'Blather::StanzaError' do
|
|
65
60
|
@err.type.must_equal @type.to_sym
|
66
61
|
end
|
67
62
|
|
68
|
-
it 'provides a
|
69
|
-
@err.must_respond_to :
|
70
|
-
@err.
|
63
|
+
it 'provides a name attribute' do
|
64
|
+
@err.must_respond_to :name
|
65
|
+
@err.name.must_equal @err_name.gsub('-','_').to_sym
|
71
66
|
end
|
72
67
|
|
73
68
|
it 'provides a text attribute' do
|
@@ -131,15 +126,9 @@ describe 'Blather::StanzaError' do
|
|
131
126
|
undefined-condition
|
132
127
|
unexpected-request
|
133
128
|
].each do |error_type|
|
134
|
-
it "
|
135
|
-
e = StanzaError.import stanza_error_node(:cancel, error_type)
|
136
|
-
klass = error_type.gsub(/^\w/) { |v| v.upcase }.gsub(/\-(\w)/) { |v| v.delete('-').upcase }
|
137
|
-
e.must_be_instance_of eval("StanzaError::#{klass}")
|
138
|
-
end
|
139
|
-
|
140
|
-
it "registers #{error_type} in the handler heirarchy" do
|
129
|
+
it "handles the name for #{error_type}" do
|
141
130
|
e = StanzaError.import stanza_error_node(:cancel, error_type)
|
142
|
-
e.
|
131
|
+
e.name.must_equal error_type.gsub('-','_').to_sym
|
143
132
|
end
|
144
133
|
end
|
145
134
|
end
|
@@ -29,11 +29,6 @@ describe 'Blather::StreamError' do
|
|
29
29
|
e = StreamError.import stream_error_node
|
30
30
|
e.must_be_kind_of StreamError
|
31
31
|
end
|
32
|
-
|
33
|
-
it 'knows what class to instantiate' do
|
34
|
-
e = StreamError.import stream_error_node
|
35
|
-
e.must_be_instance_of StreamError::InternalServerError
|
36
|
-
end
|
37
32
|
end
|
38
33
|
|
39
34
|
describe 'Blather::StreamError when instantiated' do
|
@@ -44,8 +39,8 @@ describe 'Blather::StreamError when instantiated' do
|
|
44
39
|
end
|
45
40
|
|
46
41
|
it 'provides a err_name attribute' do
|
47
|
-
@err.must_respond_to :
|
48
|
-
@err.
|
42
|
+
@err.must_respond_to :name
|
43
|
+
@err.name.must_equal @err_name.gsub('-','_').to_sym
|
49
44
|
end
|
50
45
|
|
51
46
|
it 'provides a text attribute' do
|
@@ -100,15 +95,9 @@ describe 'Each XMPP stream error type' do
|
|
100
95
|
unsupported-version
|
101
96
|
xml-not-well-formed
|
102
97
|
].each do |error_type|
|
103
|
-
it "
|
104
|
-
e = StreamError.import stream_error_node(error_type)
|
105
|
-
klass = error_type.gsub(/^\w/) { |v| v.upcase }.gsub(/\-(\w)/) { |v| v.delete('-').upcase }
|
106
|
-
e.must_be_instance_of eval("StreamError::#{klass}")
|
107
|
-
end
|
108
|
-
|
109
|
-
it "registers #{error_type} in the handler heirarchy" do
|
98
|
+
it "handles the name for #{error_type}" do
|
110
99
|
e = StreamError.import stream_error_node(error_type)
|
111
|
-
e.
|
100
|
+
e.name.must_equal error_type.gsub('-','_').to_sym
|
112
101
|
end
|
113
102
|
end
|
114
103
|
end
|