blather 0.2.3 → 0.3.0

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