blather 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.rdoc +41 -12
- data/examples/echo.rb +1 -1
- data/examples/execute.rb +0 -5
- data/examples/pubsub/cli.rb +64 -0
- data/examples/pubsub/ping_pong.rb +18 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/lib/blather.rb +35 -12
- data/lib/blather/client.rb +1 -1
- data/lib/blather/client/client.rb +19 -13
- data/lib/blather/client/dsl.rb +16 -0
- data/lib/blather/client/dsl/pubsub.rb +133 -0
- data/lib/blather/core_ext/active_support.rb +1 -117
- data/lib/blather/core_ext/active_support/inheritable_attributes.rb +117 -0
- data/lib/blather/core_ext/nokogiri.rb +35 -0
- data/lib/blather/errors.rb +3 -20
- data/lib/blather/errors/sasl_error.rb +3 -1
- data/lib/blather/errors/stanza_error.rb +10 -17
- data/lib/blather/errors/stream_error.rb +11 -14
- data/lib/blather/jid.rb +1 -0
- data/lib/blather/roster.rb +9 -0
- data/lib/blather/roster_item.rb +6 -1
- data/lib/blather/stanza.rb +35 -18
- data/lib/blather/stanza/disco.rb +7 -1
- data/lib/blather/stanza/disco/disco_info.rb +45 -33
- data/lib/blather/stanza/disco/disco_items.rb +32 -21
- data/lib/blather/stanza/iq.rb +13 -8
- data/lib/blather/stanza/iq/query.rb +16 -8
- data/lib/blather/stanza/iq/roster.rb +33 -22
- data/lib/blather/stanza/message.rb +20 -31
- data/lib/blather/stanza/presence.rb +3 -5
- data/lib/blather/stanza/presence/status.rb +13 -21
- data/lib/blather/stanza/presence/subscription.rb +11 -16
- data/lib/blather/stanza/pubsub.rb +63 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +50 -0
- data/lib/blather/stanza/pubsub/create.rb +43 -0
- data/lib/blather/stanza/pubsub/errors.rb +9 -0
- data/lib/blather/stanza/pubsub/event.rb +77 -0
- data/lib/blather/stanza/pubsub/items.rb +63 -0
- data/lib/blather/stanza/pubsub/publish.rb +58 -0
- data/lib/blather/stanza/pubsub/retract.rb +53 -0
- data/lib/blather/stanza/pubsub/subscribe.rb +42 -0
- data/lib/blather/stanza/pubsub/subscription.rb +66 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +55 -0
- data/lib/blather/stanza/pubsub/unsubscribe.rb +42 -0
- data/lib/blather/stanza/pubsub_owner.rb +41 -0
- data/lib/blather/stanza/pubsub_owner/delete.rb +34 -0
- data/lib/blather/stanza/pubsub_owner/purge.rb +34 -0
- data/lib/blather/stream.rb +76 -168
- data/lib/blather/stream/client.rb +1 -2
- data/lib/blather/stream/component.rb +9 -5
- data/lib/blather/stream/features.rb +53 -0
- data/lib/blather/stream/features/resource.rb +63 -0
- data/lib/blather/stream/{sasl.rb → features/sasl.rb} +53 -52
- data/lib/blather/stream/features/session.rb +44 -0
- data/lib/blather/stream/features/tls.rb +28 -0
- data/lib/blather/stream/parser.rb +70 -46
- data/lib/blather/xmpp_node.rb +113 -52
- data/spec/blather/client/client_spec.rb +44 -58
- data/spec/blather/client/dsl/pubsub_spec.rb +465 -0
- data/spec/blather/client/dsl_spec.rb +19 -6
- data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
- data/spec/blather/errors/sasl_error_spec.rb +8 -8
- data/spec/blather/errors/stanza_error_spec.rb +25 -33
- data/spec/blather/errors/stream_error_spec.rb +21 -16
- data/spec/blather/errors_spec.rb +4 -11
- data/spec/blather/jid_spec.rb +31 -30
- data/spec/blather/roster_item_spec.rb +34 -23
- data/spec/blather/roster_spec.rb +27 -12
- data/spec/blather/stanza/discos/disco_info_spec.rb +61 -42
- data/spec/blather/stanza/discos/disco_items_spec.rb +47 -35
- data/spec/blather/stanza/iq/query_spec.rb +34 -11
- data/spec/blather/stanza/iq/roster_spec.rb +47 -30
- data/spec/blather/stanza/iq_spec.rb +19 -14
- data/spec/blather/stanza/message_spec.rb +30 -17
- data/spec/blather/stanza/presence/status_spec.rb +43 -20
- data/spec/blather/stanza/presence/subscription_spec.rb +41 -21
- data/spec/blather/stanza/presence_spec.rb +34 -21
- data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
- data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
- data/spec/blather/stanza/pubsub/event_spec.rb +84 -0
- data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
- data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
- data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
- data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
- data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
- data/spec/blather/stanza/pubsub_spec.rb +62 -0
- data/spec/blather/stanza_spec.rb +53 -38
- data/spec/blather/stream/client_spec.rb +231 -88
- data/spec/blather/stream/component_spec.rb +14 -5
- data/spec/blather/stream/parser_spec.rb +145 -0
- data/spec/blather/xmpp_node_spec.rb +192 -96
- data/spec/fixtures/pubsub.rb +311 -0
- data/spec/spec_helper.rb +5 -4
- metadata +54 -18
- data/Rakefile +0 -139
- data/ext/extconf.rb +0 -65
- data/ext/push_parser.c +0 -209
- data/lib/blather/core_ext/libxml.rb +0 -28
- data/lib/blather/stream/resource.rb +0 -48
- data/lib/blather/stream/session.rb +0 -36
- data/lib/blather/stream/stream_handler.rb +0 -39
- data/lib/blather/stream/tls.rb +0 -33
- data/spec/blather/core_ext/libxml_spec.rb +0 -58
@@ -8,11 +8,23 @@ class Stanza
|
|
8
8
|
|
9
9
|
register :message
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
def self.import(node)
|
12
|
+
klass = nil
|
13
|
+
node.children.each { |e| break if klass = class_from_registration(e.element_name, (e.namespace.href if e.namespace)) }
|
14
|
+
|
15
|
+
if klass && klass != self
|
16
|
+
klass.import(node)
|
17
|
+
else
|
18
|
+
new(node[:type]).inherit(node)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.new(to = nil, body = nil, type = :chat)
|
23
|
+
node = super(:message)
|
24
|
+
node.to = to
|
25
|
+
node.type = type
|
26
|
+
node.body = body
|
27
|
+
node
|
16
28
|
end
|
17
29
|
|
18
30
|
VALID_TYPES.each do |valid_type|
|
@@ -26,32 +38,9 @@ class Stanza
|
|
26
38
|
super
|
27
39
|
end
|
28
40
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
def body
|
35
|
-
content_from :body
|
36
|
-
end
|
37
|
-
|
38
|
-
def subject=(subject)
|
39
|
-
remove_child :subject
|
40
|
-
self << XMPPNode.new('subject', subject) if subject
|
41
|
-
end
|
42
|
-
|
43
|
-
def subject
|
44
|
-
content_from :subject
|
45
|
-
end
|
46
|
-
|
47
|
-
def thread=(thread)
|
48
|
-
remove_child :thread
|
49
|
-
self << XMPPNode.new('thread', thread) if thread
|
50
|
-
end
|
51
|
-
|
52
|
-
def thread
|
53
|
-
content_from :thread
|
54
|
-
end
|
41
|
+
content_attr_accessor :body
|
42
|
+
content_attr_accessor :subject
|
43
|
+
content_attr_accessor :thread
|
55
44
|
end
|
56
45
|
|
57
46
|
end #Stanza
|
@@ -24,13 +24,11 @@ class Stanza
|
|
24
24
|
|
25
25
|
##
|
26
26
|
# Ensure element_name is "presence" for all subclasses
|
27
|
-
def
|
27
|
+
def self.new
|
28
28
|
super :presence
|
29
29
|
end
|
30
30
|
|
31
|
-
VALID_TYPES
|
32
|
-
define_method("#{valid_type}?") { self.type == valid_type }
|
33
|
-
end
|
31
|
+
attribute_helpers_for(:type, VALID_TYPES)
|
34
32
|
|
35
33
|
##
|
36
34
|
# Ensures type is one of :unavailable, :subscribe, :subscribed, :unsubscribe, :unsubscribed, :probe or :error
|
@@ -42,4 +40,4 @@ class Stanza
|
|
42
40
|
end
|
43
41
|
|
44
42
|
end #Stanza
|
45
|
-
end
|
43
|
+
end
|
@@ -9,12 +9,15 @@ class Presence
|
|
9
9
|
|
10
10
|
register :status, :status
|
11
11
|
|
12
|
-
def
|
13
|
-
super()
|
14
|
-
|
15
|
-
|
12
|
+
def self.new(state = nil, message = nil)
|
13
|
+
node = super()
|
14
|
+
node.state = state
|
15
|
+
node.message = message
|
16
|
+
node
|
16
17
|
end
|
17
18
|
|
19
|
+
attribute_helpers_for(:state, VALID_STATES)
|
20
|
+
|
18
21
|
##
|
19
22
|
# Ensures type is nil or :unavailable
|
20
23
|
def type=(type)
|
@@ -29,8 +32,7 @@ class Presence
|
|
29
32
|
state = nil if state == :available
|
30
33
|
raise ArgumentError, "Invalid Status (#{state}), use: #{VALID_STATES*' '}" if state && !VALID_STATES.include?(state)
|
31
34
|
|
32
|
-
|
33
|
-
self << XMPPNode.new('show', state) if state
|
35
|
+
set_content_for :show, state
|
34
36
|
end
|
35
37
|
|
36
38
|
##
|
@@ -43,23 +45,13 @@ class Presence
|
|
43
45
|
# Ensure priority is between -128 and 127
|
44
46
|
def priority=(new_priority)
|
45
47
|
raise ArgumentError, 'Priority must be between -128 and +127' if new_priority && !(-128..127).include?(new_priority.to_i)
|
46
|
-
|
47
|
-
|
48
|
-
self << XMPPNode.new('priority', new_priority) if new_priority
|
49
|
-
end
|
50
|
-
|
51
|
-
def priority
|
52
|
-
content_from(:priority).to_i
|
48
|
+
set_content_for :priority, new_priority
|
49
|
+
|
53
50
|
end
|
54
51
|
|
55
|
-
|
56
|
-
remove_child :status
|
57
|
-
self << XMPPNode.new('status', msg) if msg
|
58
|
-
end
|
52
|
+
content_attr_reader :priority, :to_i
|
59
53
|
|
60
|
-
|
61
|
-
content_from :status
|
62
|
-
end
|
54
|
+
content_attr_accessor :message, nil, :status
|
63
55
|
|
64
56
|
##
|
65
57
|
# Compare status based on priority
|
@@ -75,4 +67,4 @@ class Presence
|
|
75
67
|
|
76
68
|
end #Presence
|
77
69
|
end #Stanza
|
78
|
-
end #Blather
|
70
|
+
end #Blather
|
@@ -5,10 +5,11 @@ class Presence
|
|
5
5
|
class Subscription < Presence
|
6
6
|
register :subscription, :subscription
|
7
7
|
|
8
|
-
def
|
9
|
-
super()
|
10
|
-
|
11
|
-
|
8
|
+
def self.new(to = nil, type = nil)
|
9
|
+
node = super()
|
10
|
+
node.to = to
|
11
|
+
node.type = type
|
12
|
+
node
|
12
13
|
end
|
13
14
|
|
14
15
|
def inherit(node)
|
@@ -24,49 +25,43 @@ class Presence
|
|
24
25
|
# Create an approve stanza
|
25
26
|
def approve!
|
26
27
|
self.type = :subscribed
|
27
|
-
|
28
|
+
reply_if_needed!
|
28
29
|
end
|
29
30
|
|
30
31
|
##
|
31
32
|
# Create a refuse stanza
|
32
33
|
def refuse!
|
33
34
|
self.type = :unsubscribed
|
34
|
-
|
35
|
+
reply_if_needed!
|
35
36
|
end
|
36
37
|
|
37
38
|
##
|
38
39
|
# Create an unsubscribe stanza
|
39
40
|
def unsubscribe!
|
40
41
|
self.type = :unsubscribe
|
41
|
-
|
42
|
+
reply_if_needed!
|
42
43
|
end
|
43
44
|
|
44
45
|
##
|
45
46
|
# Create a cancel stanza
|
46
47
|
def cancel!
|
47
48
|
self.type = :unsubscribed
|
48
|
-
|
49
|
+
reply_if_needed!
|
49
50
|
end
|
50
51
|
|
51
52
|
##
|
52
53
|
# Create a request stanza
|
53
54
|
def request!
|
54
55
|
self.type = :subscribe
|
55
|
-
|
56
|
+
reply_if_needed!
|
56
57
|
end
|
57
58
|
|
58
59
|
def request?
|
59
60
|
self.type == :subscribe
|
60
61
|
end
|
61
62
|
|
62
|
-
private
|
63
|
-
def morph_to_reply
|
64
|
-
self.to = self.from if self.from
|
65
|
-
self.from = nil
|
66
|
-
self
|
67
|
-
end
|
68
63
|
end #Subscription
|
69
64
|
|
70
65
|
end #Presence
|
71
66
|
end #Stanza
|
72
|
-
end
|
67
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
|
4
|
+
class PubSub < Iq
|
5
|
+
register :pubsub_node, :pubsub, 'http://jabber.org/protocol/pubsub'
|
6
|
+
|
7
|
+
def self.import(node)
|
8
|
+
klass = nil
|
9
|
+
if pubsub = node.document.find_first('//ns:pubsub', :ns => self.registered_ns)
|
10
|
+
pubsub.children.each { |e| break if klass = class_from_registration(e.element_name, (e.namespace.href if e.namespace)) }
|
11
|
+
end
|
12
|
+
(klass || self).new(node[:type]).inherit(node)
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Ensure the namespace is set to the query node
|
17
|
+
def self.new(type = nil, host = nil)
|
18
|
+
new_node = super type
|
19
|
+
new_node.to = host
|
20
|
+
new_node.pubsub
|
21
|
+
new_node
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Kill the pubsub node before running inherit
|
26
|
+
def inherit(node)
|
27
|
+
remove_children :pubsub
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def pubsub
|
32
|
+
p = find_first('ns:pubsub', :ns => self.class.registered_ns) ||
|
33
|
+
find_first('pubsub', :ns => self.class.registered_ns)
|
34
|
+
|
35
|
+
unless p
|
36
|
+
self << (p = XMPPNode.new('pubsub', self.document))
|
37
|
+
p.namespace = self.class.registered_ns
|
38
|
+
end
|
39
|
+
p
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class PubSubItem < XMPPNode
|
44
|
+
def self.new(id = nil, payload = nil, document = nil)
|
45
|
+
new_node = super 'item', document
|
46
|
+
new_node.id = id
|
47
|
+
new_node.payload = payload if payload
|
48
|
+
new_node
|
49
|
+
end
|
50
|
+
|
51
|
+
attribute_accessor :id
|
52
|
+
|
53
|
+
def payload=(payload = nil)
|
54
|
+
self.content = payload
|
55
|
+
end
|
56
|
+
|
57
|
+
def payload
|
58
|
+
content.empty? ? nil : content
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end #Stanza
|
63
|
+
end #Blather
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
class PubSub
|
4
|
+
|
5
|
+
class Affiliations < PubSub
|
6
|
+
register :pubsub_affiliations, :affiliations, self.registered_ns
|
7
|
+
|
8
|
+
include Enumerable
|
9
|
+
alias_method :find, :xpath
|
10
|
+
|
11
|
+
def self.new(type = nil, host = nil)
|
12
|
+
new_node = super
|
13
|
+
new_node.affiliations
|
14
|
+
new_node
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Kill the affiliations node before running inherit
|
19
|
+
def inherit(node)
|
20
|
+
affiliations.remove
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def affiliations
|
25
|
+
aff = pubsub.find_first('pubsub_ns:affiliations', :pubsub_ns => self.class.registered_ns)
|
26
|
+
self.pubsub << (aff = XMPPNode.new('affiliations', self.document)) unless aff
|
27
|
+
aff
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(&block)
|
31
|
+
list.each &block
|
32
|
+
end
|
33
|
+
|
34
|
+
def size
|
35
|
+
list.size
|
36
|
+
end
|
37
|
+
|
38
|
+
def list
|
39
|
+
items = affiliations.find('//ns:affiliation', :ns => self.class.registered_ns)
|
40
|
+
items.inject({}) do |hash, item|
|
41
|
+
hash[item[:affiliation].to_sym] ||= []
|
42
|
+
hash[item[:affiliation].to_sym] << item[:node]
|
43
|
+
hash
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end #Affiliations
|
47
|
+
|
48
|
+
end #PubSub
|
49
|
+
end #Stanza
|
50
|
+
end #Blather
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
class PubSub
|
4
|
+
|
5
|
+
class Create < PubSub
|
6
|
+
register :pubsub_create, :create, self.registered_ns
|
7
|
+
|
8
|
+
def self.new(type = :set, host = nil, node = nil)
|
9
|
+
new_node = super(type, host)
|
10
|
+
new_node.create_node
|
11
|
+
new_node.configure_node
|
12
|
+
new_node.node = node
|
13
|
+
new_node
|
14
|
+
end
|
15
|
+
|
16
|
+
def node
|
17
|
+
create_node[:node]
|
18
|
+
end
|
19
|
+
|
20
|
+
def node=(node)
|
21
|
+
create_node[:node] = node
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_node
|
25
|
+
unless create_node = pubsub.find_first('ns:create', :ns => self.class.registered_ns)
|
26
|
+
self.pubsub << (create_node = XMPPNode.new('create', self.document))
|
27
|
+
create_node.namespace = self.pubsub.namespace
|
28
|
+
end
|
29
|
+
create_node
|
30
|
+
end
|
31
|
+
|
32
|
+
def configure_node
|
33
|
+
unless configure_node = pubsub.find_first('ns:configure', :ns => self.class.registered_ns)
|
34
|
+
self.pubsub << (configure_node = XMPPNode.new('configure', self.document))
|
35
|
+
configure_node.namespace = self.pubsub.namespace
|
36
|
+
end
|
37
|
+
configure_node
|
38
|
+
end
|
39
|
+
end #Retract
|
40
|
+
|
41
|
+
end #PubSub
|
42
|
+
end #Stanza
|
43
|
+
end #Blather
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
class PubSub
|
4
|
+
|
5
|
+
class Event < Message
|
6
|
+
register :pubsub_event, :event, 'http://jabber.org/protocol/pubsub#event'
|
7
|
+
|
8
|
+
##
|
9
|
+
# Ensure the event_node is created
|
10
|
+
def self.new(type = nil)
|
11
|
+
node = super
|
12
|
+
node.event_node
|
13
|
+
node
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Kill the event_node node before running inherit
|
18
|
+
def inherit(node)
|
19
|
+
event_node.remove
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def node
|
24
|
+
!purge? ? items_node[:node] : purge_node[:node]
|
25
|
+
end
|
26
|
+
|
27
|
+
def retractions
|
28
|
+
items_node.find('ns:retract', :ns => self.class.registered_ns).map { |i| i[:id] }
|
29
|
+
end
|
30
|
+
|
31
|
+
def retractions?
|
32
|
+
!retractions.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def items
|
36
|
+
items_node.find('ns:item', :ns => self.class.registered_ns).map { |i| PubSubItem.new(nil,nil,self.document).inherit i }
|
37
|
+
end
|
38
|
+
|
39
|
+
def items?
|
40
|
+
!items.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def purge?
|
44
|
+
purge_node
|
45
|
+
end
|
46
|
+
|
47
|
+
def event_node
|
48
|
+
node = find_first('ns:event', :ns => self.class.registered_ns)
|
49
|
+
node = find_first('event', self.class.registered_ns) unless node
|
50
|
+
unless node
|
51
|
+
(self << (node = XMPPNode.new('event', self.document)))
|
52
|
+
node.namespace = self.class.registered_ns
|
53
|
+
end
|
54
|
+
node
|
55
|
+
end
|
56
|
+
|
57
|
+
def items_node
|
58
|
+
node = find_first('event/ns:items', :ns => self.class.registered_ns)
|
59
|
+
unless node
|
60
|
+
(self.event_node << (node = XMPPNode.new('items', self.document)))
|
61
|
+
node.namespace = event_node.namespace
|
62
|
+
end
|
63
|
+
node
|
64
|
+
end
|
65
|
+
|
66
|
+
def purge_node
|
67
|
+
event_node.find_first('ns:purge', :ns => self.class.registered_ns)
|
68
|
+
end
|
69
|
+
|
70
|
+
def subscription_ids
|
71
|
+
find('//ns:header[@name="SubID"]', :ns => 'http://jabber.org/protocol/shim').map { |n| n.content }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end #PubSub
|
76
|
+
end #Stanza
|
77
|
+
end #Blather
|