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