blather 0.2.1 → 0.2.2
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 +54 -29
- data/Rakefile +94 -13
- data/VERSION.yml +4 -0
- data/examples/drb_client.rb +2 -4
- data/examples/echo.rb +13 -8
- data/examples/pubsub/cli.rb +64 -0
- data/examples/pubsub/ping_pong.rb +18 -0
- data/examples/pubsub/pubsub_dsl.rb +52 -0
- data/examples/pubsub_client.rb +39 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/ext/extconf.rb +65 -0
- data/lib/blather.rb +18 -121
- data/lib/blather/client.rb +13 -0
- data/lib/blather/client/client.rb +165 -0
- data/lib/blather/client/dsl.rb +99 -0
- data/lib/blather/client/pubsub.rb +53 -0
- data/lib/blather/client/pubsub/node.rb +27 -0
- data/lib/blather/core_ext/active_support.rb +1 -0
- data/lib/blather/core_ext/libxml.rb +7 -1
- data/lib/blather/errors.rb +39 -18
- 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/jid.rb +9 -16
- data/lib/blather/roster.rb +9 -0
- data/lib/blather/roster_item.rb +7 -4
- data/lib/blather/stanza.rb +19 -25
- data/lib/blather/stanza/disco.rb +9 -0
- data/lib/blather/stanza/disco/disco_info.rb +84 -0
- data/lib/blather/stanza/disco/disco_items.rb +59 -0
- data/lib/blather/stanza/iq.rb +16 -4
- data/lib/blather/stanza/iq/query.rb +6 -4
- data/lib/blather/stanza/iq/roster.rb +38 -38
- data/lib/blather/stanza/pubsub.rb +33 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +52 -0
- data/lib/blather/stanza/pubsub/errors.rb +9 -0
- data/lib/blather/stanza/pubsub/event.rb +21 -0
- data/lib/blather/stanza/pubsub/items.rb +59 -0
- data/lib/blather/stanza/pubsub/owner.rb +9 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +57 -0
- data/lib/blather/stream.rb +125 -57
- data/lib/blather/stream/client.rb +26 -0
- data/lib/blather/stream/component.rb +34 -0
- data/lib/blather/stream/parser.rb +17 -27
- data/lib/blather/stream/resource.rb +21 -24
- data/lib/blather/stream/sasl.rb +60 -37
- data/lib/blather/stream/session.rb +12 -19
- data/lib/blather/stream/stream_handler.rb +39 -0
- data/lib/blather/stream/tls.rb +22 -18
- data/lib/blather/xmpp_node.rb +91 -17
- 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/jid_spec.rb +0 -7
- data/spec/blather/roster_item_spec.rb +5 -0
- data/spec/blather/roster_spec.rb +6 -6
- data/spec/blather/stanza/discos/disco_info_spec.rb +207 -0
- data/spec/blather/stanza/discos/disco_items_spec.rb +136 -0
- data/spec/blather/stanza/iq/query_spec.rb +9 -2
- data/spec/blather/stanza/iq/roster_spec.rb +117 -1
- data/spec/blather/stanza/iq_spec.rb +29 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +12 -1
- data/spec/blather/stanza/presence_spec.rb +29 -0
- data/spec/blather/stanza/pubsub/affiliations_spec.rb +46 -0
- data/spec/blather/stanza/pubsub/items_spec.rb +59 -0
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +63 -0
- data/spec/blather/stanza/pubsub_spec.rb +26 -0
- data/spec/blather/stanza_spec.rb +13 -1
- data/spec/blather/stream/client_spec.rb +787 -0
- data/spec/blather/stream/component_spec.rb +86 -0
- data/spec/blather/xmpp_node_spec.rb +75 -22
- data/spec/fixtures/pubsub.rb +157 -0
- data/spec/spec_helper.rb +6 -14
- metadata +86 -74
- data/CHANGELOG +0 -5
- data/Manifest +0 -47
- data/blather.gemspec +0 -41
- data/lib/blather/stanza/error.rb +0 -31
- data/spec/blather/stream_spec.rb +0 -462
- data/spec/build_safe.rb +0 -20
@@ -0,0 +1,59 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
|
4
|
+
class DiscoItems < Disco
|
5
|
+
register :disco_items, nil, 'http://jabber.org/protocol/disco#items'
|
6
|
+
|
7
|
+
def initialize(type = nil, node = nil, items = [])
|
8
|
+
super type
|
9
|
+
self.node = node
|
10
|
+
[items].flatten.each do |item|
|
11
|
+
query << (item.is_a?(Item) ? item : Item.new(item[:jid], item[:node], item[:name]))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def items
|
16
|
+
items = query.find('item')
|
17
|
+
items = query.find('query_ns:item', :query_ns => self.class.ns) if items.empty?
|
18
|
+
items.map { |i| Item.new i }
|
19
|
+
end
|
20
|
+
|
21
|
+
def node=(node)
|
22
|
+
query.attributes[:node] = node
|
23
|
+
end
|
24
|
+
|
25
|
+
def node
|
26
|
+
query.attributes[:node]
|
27
|
+
end
|
28
|
+
|
29
|
+
class Item < XMPPNode
|
30
|
+
def initialize(jid, node = nil, name = nil)
|
31
|
+
super :item
|
32
|
+
|
33
|
+
if jid.is_a?(XML::Node)
|
34
|
+
self.inherit jid
|
35
|
+
else
|
36
|
+
self.jid = jid
|
37
|
+
self.node = node
|
38
|
+
self.name = name
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def jid
|
43
|
+
(j = attributes[:jid]) ? JID.new(j) : nil
|
44
|
+
end
|
45
|
+
attribute_writer :jid
|
46
|
+
|
47
|
+
attribute_accessor :node, :name, :to_sym => false
|
48
|
+
end
|
49
|
+
|
50
|
+
def eql?(other)
|
51
|
+
other.kind_of?(self.class) &&
|
52
|
+
other.jid == self.jid &&
|
53
|
+
other.node == self.node &&
|
54
|
+
other.name == self.name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end #Stanza
|
59
|
+
end #Blather
|
data/lib/blather/stanza/iq.rb
CHANGED
@@ -4,22 +4,34 @@ class Stanza
|
|
4
4
|
##
|
5
5
|
# Base Iq stanza
|
6
6
|
class Iq < Stanza
|
7
|
+
VALID_TYPES = [:get, :set, :result, :error]
|
8
|
+
|
7
9
|
register :iq
|
8
10
|
|
9
11
|
def self.import(node)
|
10
|
-
raise "Import missmatch #{[node.element_name, self.name].inspect}" if node.element_name != self.name.to_s
|
12
|
+
raise(ArgumentError, "Import missmatch #{[node.element_name, self.name].inspect}") if node.element_name != self.name.to_s
|
11
13
|
klass = nil
|
12
|
-
node.each { |e| break if klass = class_from_registration(e.element_name, e.
|
13
|
-
(klass || self).new(node[
|
14
|
+
node.children.each { |e| break if klass = class_from_registration(e.element_name, e.namespace) }
|
15
|
+
(klass || self).new(node.attributes[:type]).inherit(node)
|
14
16
|
end
|
15
17
|
|
16
18
|
def initialize(type = nil, to = nil, id = nil)
|
17
19
|
super :iq
|
18
|
-
self.xmlns = nil
|
19
20
|
self.type = type || :get
|
20
21
|
self.to = to
|
21
22
|
self.id = id if id
|
22
23
|
end
|
24
|
+
|
25
|
+
VALID_TYPES.each do |valid_type|
|
26
|
+
define_method("#{valid_type}?") { self.type == valid_type }
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Ensures type is :get, :set, :result or :error
|
31
|
+
def type=(type)
|
32
|
+
raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}" if type && !VALID_TYPES.include?(type.to_sym)
|
33
|
+
super
|
34
|
+
end
|
23
35
|
end
|
24
36
|
|
25
37
|
end #Stanza
|
@@ -9,14 +9,13 @@ class Iq
|
|
9
9
|
# Ensure the namespace is set to the query node
|
10
10
|
def initialize(type = nil)
|
11
11
|
super
|
12
|
-
query.
|
12
|
+
query.namespace = self.class.ns
|
13
13
|
end
|
14
14
|
|
15
15
|
##
|
16
16
|
# Kill the query node before running inherit
|
17
17
|
def inherit(node)
|
18
18
|
query.remove!
|
19
|
-
@query = nil
|
20
19
|
super
|
21
20
|
end
|
22
21
|
|
@@ -24,7 +23,9 @@ class Iq
|
|
24
23
|
# Query node accessor
|
25
24
|
# This will ensure there actually is a query node
|
26
25
|
def query
|
27
|
-
|
26
|
+
q = find_first('query')
|
27
|
+
q = find_first('//query_ns:query', :query_ns => self.class.ns) if !q && self.class.ns
|
28
|
+
(self << (q = XMPPNode.new('query'))) unless q
|
28
29
|
q
|
29
30
|
end
|
30
31
|
|
@@ -39,8 +40,9 @@ class Iq
|
|
39
40
|
##
|
40
41
|
# A query reply should have type set to "result"
|
41
42
|
def reply!
|
42
|
-
self.type = :result
|
43
43
|
super
|
44
|
+
self.type = :result
|
45
|
+
self
|
44
46
|
end
|
45
47
|
end #Query
|
46
48
|
|
@@ -5,27 +5,42 @@ class Iq
|
|
5
5
|
class Roster < Query
|
6
6
|
register :roster, nil, 'jabber:iq:roster'
|
7
7
|
|
8
|
+
##
|
9
|
+
# Any new items are added to the query
|
8
10
|
def initialize(type = nil, item = nil)
|
9
|
-
super
|
11
|
+
super type
|
10
12
|
query << item if item
|
11
13
|
end
|
12
14
|
|
15
|
+
##
|
16
|
+
# Inherit the XMPPNode to create a proper Roster object.
|
17
|
+
# Creates RosterItem objects out of each roster item as well.
|
13
18
|
def inherit(node)
|
19
|
+
# remove the current set of nodes
|
14
20
|
items.each { |i| i.remove! }
|
15
|
-
@items = nil
|
16
21
|
super
|
22
|
+
# transmogrify nodes into RosterItems
|
17
23
|
items.each { |i| query << RosterItem.new(i); i.remove! }
|
18
|
-
@items = nil
|
19
24
|
self
|
20
25
|
end
|
21
26
|
|
27
|
+
##
|
28
|
+
# Roster items
|
22
29
|
def items
|
23
|
-
query.find('item'
|
30
|
+
items = query.find('//item', self.class.ns)
|
31
|
+
items = query.find('//query_ns:item', :query_ns => self.class.ns) if items.empty?
|
32
|
+
items.map { |i| RosterItem.new(i) }
|
24
33
|
end
|
25
34
|
|
26
35
|
class RosterItem < XMPPNode
|
36
|
+
##
|
37
|
+
# [jid] may be either a JID or XMPPNode.
|
38
|
+
# [name] name alias of the given JID
|
39
|
+
# [subscription] subscription type
|
40
|
+
# [ask] ask subscription sub-state
|
27
41
|
def initialize(jid = nil, name = nil, subscription = nil, ask = nil)
|
28
|
-
super
|
42
|
+
super :item
|
43
|
+
|
29
44
|
if jid.is_a?(XML::Node)
|
30
45
|
self.inherit jid
|
31
46
|
else
|
@@ -36,49 +51,34 @@ class Iq
|
|
36
51
|
end
|
37
52
|
end
|
38
53
|
|
54
|
+
##
|
55
|
+
# Roster item's JID
|
39
56
|
def jid
|
40
|
-
(j = attributes[
|
41
|
-
end
|
42
|
-
|
43
|
-
def jid=(jid)
|
44
|
-
attributes['jid'] = jid
|
45
|
-
end
|
46
|
-
|
47
|
-
def name
|
48
|
-
attributes['name']
|
49
|
-
end
|
50
|
-
|
51
|
-
def name=(name)
|
52
|
-
attributes['name'] = name
|
57
|
+
(j = attributes[:jid]) ? JID.new(j) : nil
|
53
58
|
end
|
59
|
+
attribute_writer :jid
|
54
60
|
|
55
|
-
|
56
|
-
attributes['subscription'].to_sym if attributes['subscription']
|
57
|
-
end
|
58
|
-
|
59
|
-
def subscription=(subscription)
|
60
|
-
attributes['subscription'] = subscription
|
61
|
-
end
|
61
|
+
attribute_accessor :name, :to_sym => false
|
62
62
|
|
63
|
-
|
64
|
-
attributes['ask'].to_sym if attributes['ask']
|
65
|
-
end
|
66
|
-
|
67
|
-
def ask=(ask)
|
68
|
-
attributes['ask'] = ask
|
69
|
-
end
|
63
|
+
attribute_accessor :subscription, :ask
|
70
64
|
|
65
|
+
##
|
66
|
+
# The groups roster item belongs to
|
71
67
|
def groups
|
72
|
-
|
68
|
+
find(:group).map { |g| g.content }
|
73
69
|
end
|
74
70
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
71
|
+
##
|
72
|
+
# Set the roster item's groups
|
73
|
+
# must be an array
|
74
|
+
def groups=(new_groups)
|
75
|
+
find(:group).each { |g| g.remove! }
|
76
|
+
new_groups.uniq.each { |g| self << XMPPNode.new(:group, g) } if new_groups
|
80
77
|
end
|
81
78
|
|
79
|
+
##
|
80
|
+
# Convert the roster item to a proper stanza all wrapped up
|
81
|
+
# This facilitates new subscriptions
|
82
82
|
def to_stanza
|
83
83
|
Roster.new(:set, self)
|
84
84
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
|
4
|
+
class PubSub < Iq
|
5
|
+
register :pubsub, :pubsub, 'http://jabber.org/protocol/pubsub'
|
6
|
+
|
7
|
+
##
|
8
|
+
# Ensure the namespace is set to the query node
|
9
|
+
def initialize(type = nil, host = nil)
|
10
|
+
super type
|
11
|
+
self.to = host
|
12
|
+
pubsub.namespace = self.class.ns unless pubsub.namespace
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Kill the pubsub node before running inherit
|
17
|
+
def inherit(node)
|
18
|
+
pubsub.remove!
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def pubsub
|
23
|
+
unless p = find_first('//pubsub', Stanza::PubSub::Affiliations.ns)
|
24
|
+
p = XMPPNode.new('pubsub')
|
25
|
+
p.namespace = self.class.ns
|
26
|
+
self << p
|
27
|
+
end
|
28
|
+
p
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end #Stanza
|
33
|
+
end #Blather
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
class PubSub
|
4
|
+
|
5
|
+
class Affiliations < PubSub
|
6
|
+
register :pubsub_affiliations, :pubsub_affiliations, self.ns
|
7
|
+
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
def initialize(type = nil, host = nil)
|
11
|
+
super
|
12
|
+
affiliations
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Kill the affiliations node before running inherit
|
17
|
+
def inherit(node)
|
18
|
+
affiliations.remove!
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def affiliations
|
23
|
+
aff = pubsub.find_first('//pubsub_ns:affiliations', :pubsub_ns => self.class.ns)
|
24
|
+
(self.pubsub << (aff = XMPPNode.new('affiliations'))) unless aff
|
25
|
+
aff
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](affiliation)
|
29
|
+
list[affiliation]
|
30
|
+
end
|
31
|
+
|
32
|
+
def each(&block)
|
33
|
+
list.each &block
|
34
|
+
end
|
35
|
+
|
36
|
+
def size
|
37
|
+
list.size
|
38
|
+
end
|
39
|
+
|
40
|
+
def list
|
41
|
+
items = affiliations.find('//pubsub_ns:affiliation', :pubsub_ns => self.class.ns)
|
42
|
+
items.inject({}) do |hash, item|
|
43
|
+
hash[item.attributes[:affiliation].to_sym] ||= []
|
44
|
+
hash[item.attributes[:affiliation].to_sym] << item.attributes[:node]
|
45
|
+
hash
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end #Affiliations
|
49
|
+
|
50
|
+
end #PubSub
|
51
|
+
end #Stanza
|
52
|
+
end #Blather
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
class PubSub
|
4
|
+
|
5
|
+
class Event < Message
|
6
|
+
register :pubsub_event, nil, 'http://jabber.org/protocol/pubsub#event'
|
7
|
+
|
8
|
+
def items
|
9
|
+
end
|
10
|
+
|
11
|
+
class Item
|
12
|
+
attribute_accessor :id, :node, :to_sym => false
|
13
|
+
|
14
|
+
alias_method :payload, :content
|
15
|
+
alias_method :payload=, :content=
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end #PubSub
|
20
|
+
end #Stanza
|
21
|
+
end #Blather
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
class PubSub
|
4
|
+
|
5
|
+
class Items < PubSub
|
6
|
+
register :pubsub_items, :pubsub_items, self.ns
|
7
|
+
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
def self.request(host, path, list = [], max = nil)
|
11
|
+
node = self.new :get, host
|
12
|
+
|
13
|
+
node.items.attributes[:node] = path
|
14
|
+
node.items.attributes[:max_items] = max
|
15
|
+
|
16
|
+
(list || []).each do |id|
|
17
|
+
item = XMPPNode.new 'item'
|
18
|
+
item.attributes[:id] = id
|
19
|
+
node.items << item
|
20
|
+
end
|
21
|
+
|
22
|
+
node
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(type = nil, host = nil)
|
26
|
+
super
|
27
|
+
items
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Kill the items node before running inherit
|
32
|
+
def inherit(node)
|
33
|
+
items.remove!
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](id)
|
38
|
+
items[id]
|
39
|
+
end
|
40
|
+
|
41
|
+
def each(&block)
|
42
|
+
items.each &block
|
43
|
+
end
|
44
|
+
|
45
|
+
def size
|
46
|
+
items.size
|
47
|
+
end
|
48
|
+
|
49
|
+
def items
|
50
|
+
items = pubsub.find_first('//items', self.class.ns)
|
51
|
+
items = pubsub.find_first('//pubsub_ns:items', :pubsub_ns => self.class.ns) unless items
|
52
|
+
(self.pubsub << (items = XMPPNode.new('items'))) unless items
|
53
|
+
items
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end #PubSub
|
58
|
+
end #Stanza
|
59
|
+
end #Blather
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
class PubSub
|
4
|
+
|
5
|
+
class Subscriptions < PubSub
|
6
|
+
register :pubsub_subscriptions, :pubsub_subscriptions, self.ns
|
7
|
+
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
##
|
11
|
+
# Ensure the namespace is set to the query node
|
12
|
+
def initialize(type = nil, host = nil)
|
13
|
+
super type
|
14
|
+
self.to = host
|
15
|
+
subscriptions
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Kill the pubsub node before running inherit
|
20
|
+
def inherit(node)
|
21
|
+
subscriptions.remove!
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def subscriptions
|
26
|
+
aff = pubsub.find_first('//pubsub_ns:subscriptions', :pubsub_ns => self.class.ns)
|
27
|
+
(self.pubsub << (aff = XMPPNode.new('subscriptions'))) unless aff
|
28
|
+
aff
|
29
|
+
end
|
30
|
+
|
31
|
+
def [](subscription)
|
32
|
+
list[subscription]
|
33
|
+
end
|
34
|
+
|
35
|
+
def each(&block)
|
36
|
+
list.each &block
|
37
|
+
end
|
38
|
+
|
39
|
+
def size
|
40
|
+
list.size
|
41
|
+
end
|
42
|
+
|
43
|
+
def list
|
44
|
+
@subscription_list ||= begin
|
45
|
+
items = subscriptions.find('//pubsub_ns:subscription', :pubsub_ns => self.class.ns)
|
46
|
+
items.inject({}) do |hash, item|
|
47
|
+
hash[item.attributes[:subscription].to_sym] ||= []
|
48
|
+
hash[item.attributes[:subscription].to_sym] << item.attributes[:node]
|
49
|
+
hash
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end #Subscriptions
|
54
|
+
|
55
|
+
end #PubSub
|
56
|
+
end #Stanza
|
57
|
+
end #Blather
|