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.
Files changed (47) hide show
  1. data/README.rdoc +7 -4
  2. data/Rakefile +3 -1
  3. data/examples/print_heirarchy.rb +76 -0
  4. data/lib/blather.rb +3 -3
  5. data/lib/blather/client.rb +4 -247
  6. data/lib/blather/client/client.rb +168 -0
  7. data/lib/blather/client/dsl.rb +99 -0
  8. data/lib/blather/errors.rb +5 -0
  9. data/lib/blather/errors/sasl_error.rb +6 -70
  10. data/lib/blather/errors/stanza_error.rb +12 -176
  11. data/lib/blather/errors/stream_error.rb +8 -186
  12. data/lib/blather/stanza.rb +2 -3
  13. data/lib/blather/stanza/{iq/disco.rb → disco.rb} +1 -3
  14. data/lib/blather/stanza/{iq/discos → disco}/disco_info.rb +3 -5
  15. data/lib/blather/stanza/{iq/discos → disco}/disco_items.rb +0 -2
  16. data/lib/blather/stanza/iq/query.rb +1 -1
  17. data/lib/blather/stanza/iq/roster.rb +2 -2
  18. data/lib/blather/stanza/pubsub/subscriber.rb +64 -0
  19. data/lib/blather/stream.rb +13 -7
  20. data/lib/blather/stream/component.rb +1 -1
  21. data/lib/blather/stream/parser.rb +11 -4
  22. data/lib/blather/stream/resource.rb +1 -1
  23. data/lib/blather/stream/sasl.rb +15 -9
  24. data/lib/blather/xmpp_node.rb +10 -4
  25. data/spec/blather/client/client_spec.rb +4 -0
  26. data/spec/blather/client/dsl_spec.rb +4 -0
  27. data/spec/blather/client_spec.rb +0 -0
  28. data/spec/blather/errors/sasl_error_spec.rb +2 -25
  29. data/spec/blather/errors/stanza_error_spec.rb +7 -18
  30. data/spec/blather/errors/stream_error_spec.rb +4 -15
  31. data/spec/blather/stanza/{iq/discos → discos}/disco_info_spec.rb +12 -12
  32. data/spec/blather/stanza/{iq/discos → discos}/disco_items_spec.rb +1 -1
  33. data/spec/blather/stanza/iq/query_spec.rb +7 -0
  34. data/spec/blather/stanza/iq/roster_spec.rb +21 -21
  35. data/spec/blather/stanza/pubsub/subscriber_spec.rb +70 -0
  36. data/spec/blather/stanza_spec.rb +1 -7
  37. data/spec/blather/stream/client_spec.rb +36 -7
  38. data/spec/spec_helper.rb +1 -1
  39. metadata +16 -18
  40. data/ext/Makefile +0 -149
  41. data/ext/mkmf.log +0 -30
  42. data/ext/push_parser.bundle +0 -0
  43. data/ext/push_parser.o +0 -0
  44. data/lib/autotest/discover.rb +0 -1
  45. data/lib/autotest/spec.rb +0 -60
  46. data/spec/blather/stanza/pubsub/event_spec.rb +0 -13
  47. data/spec/build_safe.rb +0 -20
@@ -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(err_name_or_class, type, text = nil, extras = [])
97
- klass = (err_name_or_class.is_a?(Class) ? err_name_or_class : StanzaError.class_from_registration(err_name_or_class))
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,11 +1,9 @@
1
1
  module Blather
2
2
  class Stanza
3
- class Iq
4
3
 
5
- class Disco < Query
4
+ class Disco < Iq::Query
6
5
  attribute_accessor :node, :to_sym => false
7
6
  end
8
7
 
9
- end #Iq
10
8
  end #Stanza
11
9
  end #Blather
@@ -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, identities = [], features = [], node = 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
@@ -1,6 +1,5 @@
1
1
  module Blather
2
2
  class Stanza
3
- class Iq
4
3
 
5
4
  class DiscoItems < Disco
6
5
  register :disco_items, nil, 'http://jabber.org/protocol/disco#items'
@@ -56,6 +55,5 @@ class Iq
56
55
  end
57
56
  end
58
57
 
59
- end #Iq
60
58
  end #Stanza
61
59
  end #Blather
@@ -8,7 +8,7 @@ class Iq
8
8
  ##
9
9
  # Ensure the namespace is set to the query node
10
10
  def initialize(type = nil)
11
- super()
11
+ super
12
12
  query.namespace = self.class.ns
13
13
  end
14
14
 
@@ -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
@@ -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: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 'stream:features'
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.element_name == 'stream:stream'
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
- elem = "#{"#{prefix}:" if prefix}#{elem}"
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 elem == 'stream:stream'
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 if @current
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
@@ -18,19 +18,25 @@ class Stream # :nodoc:
18
18
  end
19
19
 
20
20
  def set_mechanism
21
- mod = case (mechanism = @mechanisms[@mechanism_idx].content)
22
- when 'DIGEST-MD5' then DigestMD5
23
- when 'PLAIN' then Plain
24
- when 'ANONYMOUS' then Anonymous
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
- return false
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', b64(@jid.node))
173
+ @stream.send auth_node('ANONYMOUS')
168
174
  end
169
175
  end #Anonymous
170
176
  end #SASL
@@ -126,10 +126,16 @@ module Blather
126
126
  self.class.import self
127
127
  end
128
128
 
129
- def namespace=(ns)
130
- if ns
131
- ns = {nil => ns} unless ns.is_a?(Hash)
132
- ns.each { |p,n| XML::Namespace.new self, p, n }
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
 
@@ -0,0 +1,4 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
2
+
3
+ describe 'Blather::Client' do
4
+ end
@@ -0,0 +1,4 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
2
+
3
+ describe 'Blather::DSL' do
4
+ end
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 "provides a class for #{error_type}" do
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.handler_heirarchy.must_equal ["sasl_#{error_type.gsub('-','_').gsub('_error','')}_error".to_sym, :sasl_error, :error]
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 err_name attribute' do
69
- @err.must_respond_to :err_name
70
- @err.err_name.must_equal @err_name
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 "provides a class for #{error_type}" do
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.handler_heirarchy.must_equal ["stanza_#{error_type.gsub('-','_').gsub('_error','')}_error".to_sym, :stanza_error, :error]
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 :err_name
48
- @err.err_name.must_equal @err_name
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 "provides a class for #{error_type}" do
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.handler_heirarchy.must_equal ["stream_#{error_type.gsub('-','_').gsub('_error','')}_error".to_sym, :stream_error, :error]
100
+ e.name.must_equal error_type.gsub('-','_').to_sym
112
101
  end
113
102
  end
114
103
  end