blather 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|