shingara-blather 0.4.8
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.md +162 -0
- data/examples/echo.rb +18 -0
- data/examples/execute.rb +16 -0
- data/examples/ping_pong.rb +37 -0
- data/examples/print_hierarchy.rb +76 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/stream_only.rb +27 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/lib/blather/client/client.rb +310 -0
- data/lib/blather/client/dsl/pubsub.rb +170 -0
- data/lib/blather/client/dsl.rb +264 -0
- data/lib/blather/client.rb +87 -0
- data/lib/blather/core_ext/nokogiri.rb +40 -0
- data/lib/blather/errors/sasl_error.rb +43 -0
- data/lib/blather/errors/stanza_error.rb +107 -0
- data/lib/blather/errors/stream_error.rb +82 -0
- data/lib/blather/errors.rb +69 -0
- data/lib/blather/jid.rb +142 -0
- data/lib/blather/roster.rb +111 -0
- data/lib/blather/roster_item.rb +122 -0
- data/lib/blather/stanza/disco/disco_info.rb +176 -0
- data/lib/blather/stanza/disco/disco_items.rb +132 -0
- data/lib/blather/stanza/disco.rb +25 -0
- data/lib/blather/stanza/iq/query.rb +53 -0
- data/lib/blather/stanza/iq/roster.rb +179 -0
- data/lib/blather/stanza/iq.rb +138 -0
- data/lib/blather/stanza/message.rb +332 -0
- data/lib/blather/stanza/presence/status.rb +212 -0
- data/lib/blather/stanza/presence/subscription.rb +101 -0
- data/lib/blather/stanza/presence.rb +163 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
- data/lib/blather/stanza/pubsub/create.rb +65 -0
- data/lib/blather/stanza/pubsub/errors.rb +18 -0
- data/lib/blather/stanza/pubsub/event.rb +123 -0
- data/lib/blather/stanza/pubsub/items.rb +103 -0
- data/lib/blather/stanza/pubsub/publish.rb +103 -0
- data/lib/blather/stanza/pubsub/retract.rb +92 -0
- data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
- data/lib/blather/stanza/pubsub/subscription.rb +134 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +81 -0
- data/lib/blather/stanza/pubsub/unsubscribe.rb +68 -0
- data/lib/blather/stanza/pubsub.rb +129 -0
- data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
- data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
- data/lib/blather/stanza/pubsub_owner.rb +51 -0
- data/lib/blather/stanza.rb +149 -0
- data/lib/blather/stream/client.rb +31 -0
- data/lib/blather/stream/component.rb +38 -0
- data/lib/blather/stream/features/resource.rb +63 -0
- data/lib/blather/stream/features/sasl.rb +187 -0
- data/lib/blather/stream/features/session.rb +44 -0
- data/lib/blather/stream/features/tls.rb +28 -0
- data/lib/blather/stream/features.rb +53 -0
- data/lib/blather/stream/parser.rb +102 -0
- data/lib/blather/stream.rb +231 -0
- data/lib/blather/xmpp_node.rb +218 -0
- data/lib/blather.rb +78 -0
- data/spec/blather/client/client_spec.rb +559 -0
- data/spec/blather/client/dsl/pubsub_spec.rb +462 -0
- data/spec/blather/client/dsl_spec.rb +143 -0
- data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
- data/spec/blather/errors/sasl_error_spec.rb +33 -0
- data/spec/blather/errors/stanza_error_spec.rb +129 -0
- data/spec/blather/errors/stream_error_spec.rb +108 -0
- data/spec/blather/errors_spec.rb +33 -0
- data/spec/blather/jid_spec.rb +87 -0
- data/spec/blather/roster_item_spec.rb +96 -0
- data/spec/blather/roster_spec.rb +103 -0
- data/spec/blather/stanza/discos/disco_info_spec.rb +226 -0
- data/spec/blather/stanza/discos/disco_items_spec.rb +148 -0
- data/spec/blather/stanza/iq/query_spec.rb +64 -0
- data/spec/blather/stanza/iq/roster_spec.rb +140 -0
- data/spec/blather/stanza/iq_spec.rb +45 -0
- data/spec/blather/stanza/message_spec.rb +132 -0
- data/spec/blather/stanza/presence/status_spec.rb +132 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +105 -0
- data/spec/blather/stanza/presence_spec.rb +66 -0
- 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 +67 -0
- data/spec/blather/stanza_spec.rb +116 -0
- data/spec/blather/stream/client_spec.rb +1011 -0
- data/spec/blather/stream/component_spec.rb +95 -0
- data/spec/blather/stream/parser_spec.rb +145 -0
- data/spec/blather/xmpp_node_spec.rb +231 -0
- data/spec/fixtures/pubsub.rb +311 -0
- data/spec/spec_helper.rb +43 -0
- metadata +249 -0
data/lib/blather/jid.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
module Blather
|
2
|
+
|
3
|
+
# Jabber ID or JID
|
4
|
+
#
|
5
|
+
# See [RFC 3920 Section 3 - Addressing](http://xmpp.org/rfcs/rfc3920.html#addressing)
|
6
|
+
#
|
7
|
+
# An entity is anything that can be considered a network endpoint (i.e., an
|
8
|
+
# ID on the network) and that can communicate using XMPP. All such entities
|
9
|
+
# are uniquely addressable in a form that is consistent with RFC 2396 [URI].
|
10
|
+
# For historical reasons, the address of an XMPP entity is called a Jabber
|
11
|
+
# Identifier or JID. A valid JID contains a set of ordered elements formed
|
12
|
+
# of a domain identifier, node identifier, and resource identifier.
|
13
|
+
#
|
14
|
+
# The syntax for a JID is defined below using the Augmented Backus-Naur Form
|
15
|
+
# as defined in [ABNF]. (The IPv4address and IPv6address rules are defined
|
16
|
+
# in Appendix B of [IPv6]; the allowable character sequences that conform to
|
17
|
+
# the node rule are defined by the Nodeprep profile of [STRINGPREP] as
|
18
|
+
# documented in Appendix A of this memo; the allowable character sequences
|
19
|
+
# that conform to the resource rule are defined by the Resourceprep profile
|
20
|
+
# of [STRINGPREP] as documented in Appendix B of this memo; and the
|
21
|
+
# sub-domain rule makes reference to the concept of an internationalized
|
22
|
+
# domain label as described in [IDNA].)
|
23
|
+
#
|
24
|
+
# jid = [ node "@" ] domain [ "/" resource ]
|
25
|
+
# domain = fqdn / address-literal
|
26
|
+
# fqdn = (sub-domain 1*("." sub-domain))
|
27
|
+
# sub-domain = (internationalized domain label)
|
28
|
+
# address-literal = IPv4address / IPv6address
|
29
|
+
#
|
30
|
+
# All JIDs are based on the foregoing structure. The most common use of this
|
31
|
+
# structure is to identify an instant messaging user, the server to which
|
32
|
+
# the user connects, and the user's connected resource (e.g., a specific
|
33
|
+
# client) in the form of <user@host/resource>. However, node types other
|
34
|
+
# than clients are possible; for example, a specific chat room offered by a
|
35
|
+
# multi-user chat service could be addressed as <room@service> (where "room"
|
36
|
+
# is the name of the chat room and "service" is the hostname of the
|
37
|
+
# multi-user chat service) and a specific occupant of such a room could be
|
38
|
+
# addressed as <room@service/nick> (where "nick" is the occupant's room
|
39
|
+
# nickname). Many other JID types are possible (e.g., <domain/resource>
|
40
|
+
# could be a server-side script or service).
|
41
|
+
#
|
42
|
+
# Each allowable portion of a JID (node identifier, domain identifier, and
|
43
|
+
# resource identifier) MUST NOT be more than 1023 bytes in length, resulting
|
44
|
+
# in a maximum total size (including the '@' and '/' separators) of 3071
|
45
|
+
# bytes.
|
46
|
+
class JID
|
47
|
+
include Comparable
|
48
|
+
|
49
|
+
PATTERN = /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/.freeze
|
50
|
+
|
51
|
+
attr_reader :node,
|
52
|
+
:domain,
|
53
|
+
:resource
|
54
|
+
|
55
|
+
def self.new(node, domain = nil, resource = nil)
|
56
|
+
node.is_a?(JID) ? node : super
|
57
|
+
end
|
58
|
+
|
59
|
+
# Create a new JID object
|
60
|
+
#
|
61
|
+
# @overload initialize(jid)
|
62
|
+
# Passes the jid object right back out
|
63
|
+
# @param [Blather::JID] jid a jid object
|
64
|
+
# @overload initialize(jid)
|
65
|
+
# Creates a new JID parsed out of the provided jid
|
66
|
+
# @param [String] jid a jid in the standard format
|
67
|
+
# ("node@domain/resource")
|
68
|
+
# @overload initialize(node, domain = nil, resource = nil)
|
69
|
+
# Creates a new JID
|
70
|
+
# @param [String] node the node of the JID
|
71
|
+
# @param [String, nil] domian the domain of the JID
|
72
|
+
# @param [String, nil] resource the resource of the JID
|
73
|
+
# @raise [ArgumentError] if the parts of the JID are too large (1023 bytes)
|
74
|
+
# @return [Blather::JID] a new jid object
|
75
|
+
def initialize(node, domain = nil, resource = nil)
|
76
|
+
@resource = resource
|
77
|
+
@domain = domain
|
78
|
+
@node = node
|
79
|
+
|
80
|
+
if @domain.nil? && @resource.nil?
|
81
|
+
@node, @domain, @resource = @node.to_s.scan(PATTERN).first
|
82
|
+
end
|
83
|
+
|
84
|
+
@node.downcase! if @node
|
85
|
+
@domain.downcase! if @domain
|
86
|
+
|
87
|
+
raise ArgumentError, 'Node too long' if (@node || '').length > 1023
|
88
|
+
raise ArgumentError, 'Domain too long' if (@domain || '').length > 1023
|
89
|
+
raise ArgumentError, 'Resource too long' if (@resource || '').length > 1023
|
90
|
+
end
|
91
|
+
|
92
|
+
# Turn the JID into a string
|
93
|
+
#
|
94
|
+
# * ""
|
95
|
+
# * "domain"
|
96
|
+
# * "node@domain"
|
97
|
+
# * "domain/resource"
|
98
|
+
# * "node@domain/resource"
|
99
|
+
#
|
100
|
+
# @return [String] the JID as a string
|
101
|
+
def to_s
|
102
|
+
s = @domain
|
103
|
+
s = "#{@node}@#{s}" if @node
|
104
|
+
s = "#{s}/#{@resource}" if @resource
|
105
|
+
s
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns a new JID with resource removed.
|
109
|
+
#
|
110
|
+
# @return [Blather::JID] a new JID without a resource
|
111
|
+
def stripped
|
112
|
+
dup.strip!
|
113
|
+
end
|
114
|
+
|
115
|
+
# Removes the resource (sets it to nil)
|
116
|
+
#
|
117
|
+
# @return [Blather::JID] the JID without a resource
|
118
|
+
def strip!
|
119
|
+
@resource = nil
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
# Compare two JIDs, helpful for sorting etc.
|
124
|
+
#
|
125
|
+
# String representations are compared, see JID#to_s
|
126
|
+
#
|
127
|
+
# @param [#to_s] other a JID to comare against
|
128
|
+
# @return [Fixnum<-1, 0, 1>]
|
129
|
+
def <=>(other)
|
130
|
+
to_s <=> other.to_s
|
131
|
+
end
|
132
|
+
alias_method :eql?, :==
|
133
|
+
|
134
|
+
# Test if JID is stripped
|
135
|
+
#
|
136
|
+
# @return [true, false]
|
137
|
+
def stripped?
|
138
|
+
@resource.nil?
|
139
|
+
end
|
140
|
+
end # JID
|
141
|
+
|
142
|
+
end # Blather
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Blather
|
2
|
+
|
3
|
+
# Local Roster
|
4
|
+
# Takes care of adding/removing JIDs through the stream
|
5
|
+
class Roster
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# Create a new roster
|
9
|
+
#
|
10
|
+
# @param [Blather::Stream] stream the stream the roster should use to
|
11
|
+
# update roster entries
|
12
|
+
# @param [Blather::Stanza::Roster] stanza a roster stanza used to preload
|
13
|
+
# the roster
|
14
|
+
# @return [Blather::Roster]
|
15
|
+
def initialize(stream, stanza = nil)
|
16
|
+
@stream = stream
|
17
|
+
@items = {}
|
18
|
+
stanza.items.each { |i| push i, false } if stanza
|
19
|
+
end
|
20
|
+
|
21
|
+
# Process any incoming stanzas and either adds or removes the
|
22
|
+
# corresponding RosterItem
|
23
|
+
#
|
24
|
+
# @param [Blather::Stanza::Roster] stanza a roster stanza
|
25
|
+
def process(stanza)
|
26
|
+
stanza.items.each do |i|
|
27
|
+
case i.subscription
|
28
|
+
when :remove then @items.delete(key(i.jid))
|
29
|
+
else @items[key(i.jid)] = RosterItem.new(i)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Pushes a JID into the roster
|
35
|
+
#
|
36
|
+
# @param [String, Blather::JID, #jid] elem a JID to add to the roster
|
37
|
+
# @return [self]
|
38
|
+
# @see #push
|
39
|
+
def <<(elem)
|
40
|
+
push elem
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# Push a JID into the roster and update the server
|
45
|
+
#
|
46
|
+
# @param [String, Blather::JID, #jid] elem a jid to add to the roster
|
47
|
+
# @param [true, false] send send the update over the wire
|
48
|
+
# @see Blather::JID
|
49
|
+
def push(elem, send = true)
|
50
|
+
jid = elem.respond_to?(:jid) ? elem.jid : JID.new(elem)
|
51
|
+
@items[key(jid)] = node = RosterItem.new(elem)
|
52
|
+
|
53
|
+
@stream.write(node.to_stanza(:set)) if send
|
54
|
+
end
|
55
|
+
alias_method :add, :push
|
56
|
+
|
57
|
+
# Remove a JID from the roster and update the server
|
58
|
+
#
|
59
|
+
# @param [String, Blather::JID] jid the JID to remove from the roster
|
60
|
+
def delete(jid)
|
61
|
+
@items.delete key(jid)
|
62
|
+
item = Stanza::Iq::Roster::RosterItem.new(jid, nil, :remove)
|
63
|
+
@stream.write Stanza::Iq::Roster.new(:set, item)
|
64
|
+
end
|
65
|
+
alias_method :remove, :delete
|
66
|
+
|
67
|
+
# Get a RosterItem by JID
|
68
|
+
#
|
69
|
+
# @param [String, Blather::JID] jid the jid of the item to return
|
70
|
+
# @return [Blather::RosterItem, nil] the associated RosterItem
|
71
|
+
def [](jid)
|
72
|
+
items[key(jid)]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Iterate over all RosterItems
|
76
|
+
#
|
77
|
+
# @yield [Blather::RosterItem] yields each RosterItem
|
78
|
+
def each(&block)
|
79
|
+
items.each &block
|
80
|
+
end
|
81
|
+
|
82
|
+
# Get a duplicate of all RosterItems
|
83
|
+
#
|
84
|
+
# @return [Array<Blather::RosterItem>] a duplicate of all RosterItems
|
85
|
+
def items
|
86
|
+
@items.dup
|
87
|
+
end
|
88
|
+
|
89
|
+
# A hash of items keyed by group
|
90
|
+
#
|
91
|
+
# @return [Hash<group => Array<RosterItem>>]
|
92
|
+
def grouped
|
93
|
+
self.inject(Hash.new{|h,k|h[k]=[]}) do |hash, item|
|
94
|
+
item[1].groups.each { |group| hash[group] << item[1] }
|
95
|
+
hash
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
# Creates a stripped jid
|
101
|
+
def self.key(jid)
|
102
|
+
JID.new(jid).stripped.to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
# Instance method to wrap around the class method
|
106
|
+
def key(jid)
|
107
|
+
self.class.key(jid)
|
108
|
+
end
|
109
|
+
end # Roster
|
110
|
+
|
111
|
+
end # Blather
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Blather
|
2
|
+
|
3
|
+
# RosterItems hold internal representations of the user's roster
|
4
|
+
# including each JID's status.
|
5
|
+
class RosterItem
|
6
|
+
VALID_SUBSCRIPTION_TYPES = [:both, :from, :none, :remove, :to].freeze
|
7
|
+
|
8
|
+
attr_reader :jid,
|
9
|
+
:ask,
|
10
|
+
:statuses
|
11
|
+
|
12
|
+
attr_accessor :name,
|
13
|
+
:groups
|
14
|
+
|
15
|
+
def self.new(item)
|
16
|
+
return item if item.is_a?(self)
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create a new RosterItem
|
21
|
+
#
|
22
|
+
# @overload initialize(jid)
|
23
|
+
# Create a new RosterItem based on a JID
|
24
|
+
# @param [Blather::JID] jid the JID object
|
25
|
+
# @overload initialize(jid)
|
26
|
+
# Create a new RosterItem based on a JID string
|
27
|
+
# @param [String] jid a JID string
|
28
|
+
# @overload initialize(node)
|
29
|
+
# Create a new RosterItem based on a stanza
|
30
|
+
# @param [Blather::Stanza::Iq::Roster::RosterItem] node a RosterItem
|
31
|
+
# stanza
|
32
|
+
# @return [Blather::RosterItem] the new RosterItem
|
33
|
+
def initialize(item)
|
34
|
+
@statuses = []
|
35
|
+
@groups = []
|
36
|
+
|
37
|
+
case item
|
38
|
+
when JID
|
39
|
+
self.jid = item.stripped
|
40
|
+
when String
|
41
|
+
self.jid = JID.new(item).stripped
|
42
|
+
when XMPPNode
|
43
|
+
self.jid = JID.new(item[:jid]).stripped
|
44
|
+
self.name = item[:name]
|
45
|
+
self.subscription = item[:subscription]
|
46
|
+
self.ask = item[:ask]
|
47
|
+
item.groups.each { |g| @groups << g }
|
48
|
+
end
|
49
|
+
|
50
|
+
@groups = [nil] if @groups.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set the jid
|
54
|
+
#
|
55
|
+
# @param [String, Blather::JID] jid the new jid
|
56
|
+
# @see Blather::JID
|
57
|
+
def jid=(jid)
|
58
|
+
@jid = JID.new(jid).stripped
|
59
|
+
end
|
60
|
+
|
61
|
+
# Set the subscription
|
62
|
+
# Ensures it is one of VALID_SUBSCRIPTION_TYPES
|
63
|
+
#
|
64
|
+
# @param [#to_sym] sub the new subscription
|
65
|
+
def subscription=(sub)
|
66
|
+
if sub && !VALID_SUBSCRIPTION_TYPES.include?(sub = sub.to_sym)
|
67
|
+
raise ArgumentError, "Invalid Type (#{sub}), use: #{VALID_SUBSCRIPTION_TYPES*' '}"
|
68
|
+
end
|
69
|
+
@subscription = sub ? sub : :none
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get the current subscription
|
73
|
+
#
|
74
|
+
# @return [:both, :from, :none, :remove, :to]
|
75
|
+
def subscription
|
76
|
+
@subscription || :none
|
77
|
+
end
|
78
|
+
|
79
|
+
# Set the ask value
|
80
|
+
#
|
81
|
+
# @param [nil, :subscribe] ask the new ask
|
82
|
+
def ask=(ask)
|
83
|
+
if ask && (ask = ask.to_sym) != :subscribe
|
84
|
+
raise ArgumentError, "Invalid Type (#{ask}), can only be :subscribe"
|
85
|
+
end
|
86
|
+
@ask = ask ? ask : nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# Set the status then sorts them according to priority
|
90
|
+
#
|
91
|
+
# @param [Blather::Stanza::Status] the new status
|
92
|
+
def status=(presence)
|
93
|
+
@statuses.delete_if { |s| s.from == presence.from }
|
94
|
+
@statuses << presence
|
95
|
+
@statuses.sort!
|
96
|
+
end
|
97
|
+
|
98
|
+
# The status with the highest priority
|
99
|
+
#
|
100
|
+
# @param [String, nil] resource the resource to get the status of
|
101
|
+
def status(resource = nil)
|
102
|
+
top = if resource
|
103
|
+
@statuses.detect { |s| s.from.resource == resource }
|
104
|
+
else
|
105
|
+
@statuses.first
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Translate the RosterItem into a proper stanza that can be sent over the
|
110
|
+
# stream
|
111
|
+
#
|
112
|
+
# @return [Blather::Stanza::Iq::Roster]
|
113
|
+
def to_stanza(type = nil)
|
114
|
+
r = Stanza::Iq::Roster.new type
|
115
|
+
n = Stanza::Iq::Roster::RosterItem.new jid, name, subscription, ask
|
116
|
+
r.query << n
|
117
|
+
n.groups = groups
|
118
|
+
r
|
119
|
+
end
|
120
|
+
end #RosterItem
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
|
4
|
+
# # DiscoInfo Stanza
|
5
|
+
#
|
6
|
+
# [XEP-0030 Disco Info](http://xmpp.org/extensions/xep-0030.html#info)
|
7
|
+
#
|
8
|
+
# Disco Info node that provides or retreives information about a jabber entity
|
9
|
+
#
|
10
|
+
# @handler :disco_info
|
11
|
+
class DiscoInfo < Disco
|
12
|
+
register :disco_info, nil, 'http://jabber.org/protocol/disco#info'
|
13
|
+
|
14
|
+
# Create a new DiscoInfo stanza
|
15
|
+
# @param [:get, :set, :result, :error, nil] type the Iq stanza type
|
16
|
+
# @param [String, nil] node the name of the node the info belongs to
|
17
|
+
# @param [Array<Array, DiscoInfo::Identity>, nil] identities a list of
|
18
|
+
# identities. these are passed directly to DiscoInfo::Identity.new
|
19
|
+
# @param [Array<Array, DiscoInfo::Identity>, nil] features a list of
|
20
|
+
# features. these are passed directly to DiscoInfo::Feature.new
|
21
|
+
# @return [DiscoInfo] a new DiscoInfo stanza
|
22
|
+
def self.new(type = nil, node = nil, identities = [], features = [])
|
23
|
+
new_node = super type
|
24
|
+
new_node.node = node
|
25
|
+
[identities].flatten.each { |i| new_node.query << Identity.new(i) }
|
26
|
+
[features].flatten.each { |f| new_node.query << Feature.new(f) }
|
27
|
+
new_node
|
28
|
+
end
|
29
|
+
|
30
|
+
# List of identity objects
|
31
|
+
def identities
|
32
|
+
query.find('//ns:identity', :ns => self.class.registered_ns).map do |i|
|
33
|
+
Identity.new i
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# List of feature objects
|
38
|
+
def features
|
39
|
+
query.find('//ns:feature', :ns => self.class.registered_ns).map do |f|
|
40
|
+
Feature.new f
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Identity < XMPPNode
|
45
|
+
# Create a new DiscoInfo Identity
|
46
|
+
# @overload new(node)
|
47
|
+
# Imports the XML::Node to create a Identity object
|
48
|
+
# @param [XML::Node] node the node object to import
|
49
|
+
# @overload new(opts = {})
|
50
|
+
# Creates a new Identity using a hash of options
|
51
|
+
# @param [Hash] opts a hash of options
|
52
|
+
# @option opts [String] :name the name of the identity
|
53
|
+
# @option opts [String] :type the type of the identity
|
54
|
+
# @option opts [String] :category the category of the identity
|
55
|
+
# @overload new(name, type = nil, category = nil)
|
56
|
+
# Create a new Identity by name
|
57
|
+
# @param [String] name the name of the Identity
|
58
|
+
# @param [String, nil] type the type of the Identity
|
59
|
+
# @param [String, nil] category the category of the Identity
|
60
|
+
def self.new(name, type = nil, category = nil)
|
61
|
+
new_node = super :identity
|
62
|
+
|
63
|
+
case name
|
64
|
+
when Nokogiri::XML::Node
|
65
|
+
new_node.inherit name
|
66
|
+
when Hash
|
67
|
+
new_node.name = name[:name]
|
68
|
+
new_node.type = name[:type]
|
69
|
+
new_node.category = name[:category]
|
70
|
+
else
|
71
|
+
new_node.name = name
|
72
|
+
new_node.type = type
|
73
|
+
new_node.category = category
|
74
|
+
end
|
75
|
+
new_node
|
76
|
+
end
|
77
|
+
|
78
|
+
# The Identity's category
|
79
|
+
# @return [Symbol, nil]
|
80
|
+
def category
|
81
|
+
read_attr :category, :to_sym
|
82
|
+
end
|
83
|
+
|
84
|
+
# Set the Identity's category
|
85
|
+
# @param [String, Symbol] category the new category
|
86
|
+
def category=(category)
|
87
|
+
write_attr :category, category
|
88
|
+
end
|
89
|
+
|
90
|
+
# The Identity's type
|
91
|
+
# @return [Symbol, nil]
|
92
|
+
def type
|
93
|
+
read_attr :type, :to_sym
|
94
|
+
end
|
95
|
+
|
96
|
+
# Set the Identity's type
|
97
|
+
# @param [String, Symbol] type the new category
|
98
|
+
def type=(type)
|
99
|
+
write_attr :type, type
|
100
|
+
end
|
101
|
+
|
102
|
+
# The Identity's name
|
103
|
+
# @return [String]
|
104
|
+
def name
|
105
|
+
read_attr :name
|
106
|
+
end
|
107
|
+
|
108
|
+
# Set the Identity's name
|
109
|
+
# @param [String] name the new name for the identity
|
110
|
+
def name=(name)
|
111
|
+
write_attr :name, name
|
112
|
+
end
|
113
|
+
|
114
|
+
# Compare two Identity objects by name, type and category
|
115
|
+
# @param [DiscoInfo::Identity] o the Identity object to compare against
|
116
|
+
# @return [true, false]
|
117
|
+
def eql?(o)
|
118
|
+
unless o.is_a?(self.class)
|
119
|
+
raise "Cannot compare #{self.class} with #{o.class}"
|
120
|
+
end
|
121
|
+
|
122
|
+
o.name == self.name &&
|
123
|
+
o.type == self.type &&
|
124
|
+
o.category == self.category
|
125
|
+
end
|
126
|
+
alias_method :==, :eql?
|
127
|
+
end # Identity
|
128
|
+
|
129
|
+
class Feature < XMPPNode
|
130
|
+
# Create a new DiscoInfo::Feature object
|
131
|
+
# @overload new(node)
|
132
|
+
# Create a new Feature by importing an XML::Node
|
133
|
+
# @param [XML::Node] node an XML::Node to import
|
134
|
+
# @overload new(var)
|
135
|
+
# Create a new feature by var
|
136
|
+
# @param [String] var a the Feautre's var
|
137
|
+
# @return [DiscoInfo::Feature]
|
138
|
+
def self.new(var)
|
139
|
+
new_node = super :feature
|
140
|
+
case var
|
141
|
+
when Nokogiri::XML::Node
|
142
|
+
new_node.inherit var
|
143
|
+
else
|
144
|
+
new_node.var = var
|
145
|
+
end
|
146
|
+
new_node
|
147
|
+
end
|
148
|
+
|
149
|
+
# The Feature's var
|
150
|
+
# @return [String]
|
151
|
+
def var
|
152
|
+
read_attr :var
|
153
|
+
end
|
154
|
+
|
155
|
+
# Set the Feature's var
|
156
|
+
# @param [String] var the new var
|
157
|
+
def var=(var)
|
158
|
+
write_attr :var, var
|
159
|
+
end
|
160
|
+
|
161
|
+
# Compare two Feature objects by var
|
162
|
+
# @param [DiscoInfo::Feature] o the Feature object to compare against
|
163
|
+
# @return [true, false]
|
164
|
+
def eql?(o)
|
165
|
+
unless o.is_a?(self.class)
|
166
|
+
raise "Cannot compare #{self.class} with #{o.class}"
|
167
|
+
end
|
168
|
+
|
169
|
+
o.var == self.var
|
170
|
+
end
|
171
|
+
alias_method :==, :eql?
|
172
|
+
end
|
173
|
+
end # Feature
|
174
|
+
|
175
|
+
end # Stanza
|
176
|
+
end # Blather
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
|
4
|
+
# # DiscoItems Stanza
|
5
|
+
#
|
6
|
+
# [XEP-0030 Disco Info](http://xmpp.org/extensions/xep-0030.html#items)
|
7
|
+
#
|
8
|
+
# Disco Items node that provides or retreives items associated with a
|
9
|
+
# jabbery entity
|
10
|
+
#
|
11
|
+
# @handler :disco_items
|
12
|
+
class DiscoItems < Disco
|
13
|
+
register :disco_items, nil, 'http://jabber.org/protocol/disco#items'
|
14
|
+
|
15
|
+
# Create a new DiscoItems node
|
16
|
+
#
|
17
|
+
# @param [#to_s] type the IQ type
|
18
|
+
# @param [#to_s] node the node the items are associated with
|
19
|
+
# @param [Array<Blather::XMPPNode>] items an array of Disco::Items
|
20
|
+
# @return [Blather::Stanza::DiscoItems]
|
21
|
+
def self.new(type = nil, node = nil, items = [])
|
22
|
+
new_node = super type
|
23
|
+
new_node.node = node
|
24
|
+
[items].flatten.each { |item| new_node.query << Item.new(item) }
|
25
|
+
new_node
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set of items associated with the node
|
29
|
+
#
|
30
|
+
# @return [Array<Blather::Stanza::DiscoItems::Item>]
|
31
|
+
def items
|
32
|
+
query.find('//ns:item', :ns => self.class.registered_ns).map do |i|
|
33
|
+
Item.new i
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# An individual Disco Item
|
38
|
+
class Item < XMPPNode
|
39
|
+
# Create a new Blather::Stanza::DiscoItems::Item
|
40
|
+
#
|
41
|
+
# @overload new(node)
|
42
|
+
# Create a new Item by inheriting an existing node
|
43
|
+
# @param [XML::Node] node an XML::Node to inherit from
|
44
|
+
# @overload new(opts)
|
45
|
+
# Create a new Item through a hash of options
|
46
|
+
# @param [Hash] opts a hash options
|
47
|
+
# @option opts [Blather::JID, String] :jid the JID to attach to the item
|
48
|
+
# @option opts [#to_s] :node the node the item is attached to
|
49
|
+
# @option opts [#to_S] :name the name of the Item
|
50
|
+
# @overload new(jid, node = nil, name = nil)
|
51
|
+
# Create a new Item
|
52
|
+
# @param [Blather::JID, String] jid the JID to attach to the item
|
53
|
+
# @param [#to_s] node the node the item is attached to
|
54
|
+
# @param [#to_s] name the name of the Item
|
55
|
+
def self.new(jid, node = nil, name = nil)
|
56
|
+
new_node = super :item
|
57
|
+
|
58
|
+
case jid
|
59
|
+
when Nokogiri::XML::Node
|
60
|
+
new_node.inherit jid
|
61
|
+
when Hash
|
62
|
+
new_node.jid = jid[:jid]
|
63
|
+
new_node.node = jid[:node]
|
64
|
+
new_node.name = jid[:name]
|
65
|
+
else
|
66
|
+
new_node.jid = jid
|
67
|
+
new_node.node = node
|
68
|
+
new_node.name = name
|
69
|
+
end
|
70
|
+
new_node
|
71
|
+
end
|
72
|
+
|
73
|
+
# Get the JID attached to the node
|
74
|
+
#
|
75
|
+
# @return [Blather::JID, nil]
|
76
|
+
def jid
|
77
|
+
(j = self[:jid]) ? JID.new(j) : nil
|
78
|
+
end
|
79
|
+
|
80
|
+
# Set the JID of the node
|
81
|
+
#
|
82
|
+
# @param [Blather::JID, String, nil] jid the new JID
|
83
|
+
def jid=(jid)
|
84
|
+
write_attr :jid, jid
|
85
|
+
end
|
86
|
+
|
87
|
+
# Get the name of the node
|
88
|
+
#
|
89
|
+
# @return [String, nil]
|
90
|
+
def node
|
91
|
+
read_attr :node
|
92
|
+
end
|
93
|
+
|
94
|
+
# Set the name of the node
|
95
|
+
#
|
96
|
+
# @param [String, nil] node the new node name
|
97
|
+
def node=(node)
|
98
|
+
write_attr :node, node
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get the Item name
|
102
|
+
#
|
103
|
+
# @return [String, nil]
|
104
|
+
def name
|
105
|
+
read_attr :name
|
106
|
+
end
|
107
|
+
|
108
|
+
# Set the Item name
|
109
|
+
#
|
110
|
+
# @param [#to_s] name the Item name
|
111
|
+
def name=(name)
|
112
|
+
write_attr :name, name
|
113
|
+
end
|
114
|
+
|
115
|
+
# Check for equality based on jid, node, and name
|
116
|
+
#
|
117
|
+
# @param [Blather::Stanza::DiscoItems::Item] o the other Item
|
118
|
+
def eql?(o)
|
119
|
+
unless o.is_a?(self.class)
|
120
|
+
raise "Cannot compare #{self.class} with #{o.class}"
|
121
|
+
end
|
122
|
+
|
123
|
+
o.jid == self.jid &&
|
124
|
+
o.node == self.node &&
|
125
|
+
o.name == self.name
|
126
|
+
end
|
127
|
+
alias_method :==, :eql?
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end #Stanza
|
132
|
+
end #Blather
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
|
4
|
+
# # Disco Base class
|
5
|
+
#
|
6
|
+
# Use Blather::Stanza::DiscoInfo or Blather::Stanza::DiscoItems
|
7
|
+
class Disco < Iq::Query
|
8
|
+
|
9
|
+
# Get the name of the node
|
10
|
+
#
|
11
|
+
# @return [String] the node name
|
12
|
+
def node
|
13
|
+
query[:node]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set the name of the node
|
17
|
+
#
|
18
|
+
# @param [#to_s] node the new node name
|
19
|
+
def node=(node)
|
20
|
+
query[:node] = node
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end # Stanza
|
25
|
+
end # Blather
|