blather 0.4.16 → 0.5.0
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/.gemtest +0 -0
- data/CHANGELOG +12 -5
- data/README.md +1 -1
- data/Rakefile +1 -3
- data/blather.gemspec +11 -24
- data/examples/echo.rb +1 -0
- data/examples/execute.rb +1 -0
- data/examples/ping_pong.rb +1 -0
- data/examples/print_hierarchy.rb +1 -0
- data/examples/rosterprint.rb +1 -0
- data/examples/stream_only.rb +1 -0
- data/examples/xmpp4r/echo.rb +1 -0
- data/lib/blather.rb +5 -1
- data/lib/blather/client/client.rb +29 -154
- data/lib/blather/client/dsl.rb +9 -3
- data/lib/blather/client/dsl/pubsub.rb +2 -0
- data/lib/blather/core_ext/eventmachine.rb +4 -1
- data/lib/blather/core_ext/ipaddr.rb +2 -1
- data/lib/blather/core_ext/nokogiri.rb +3 -1
- data/lib/blather/errors/sasl_error.rb +1 -0
- data/lib/blather/errors/stanza_error.rb +3 -1
- data/lib/blather/errors/stream_error.rb +1 -0
- data/lib/blather/file_transfer.rb +4 -1
- data/lib/blather/file_transfer/s5b.rb +3 -4
- data/lib/blather/jid.rb +2 -0
- data/lib/blather/roster_item.rb +13 -3
- data/lib/blather/stanza.rb +11 -3
- data/lib/blather/stanza/disco/capabilities.rb +161 -0
- data/lib/blather/stanza/disco/disco_info.rb +3 -1
- data/lib/blather/stanza/disco/disco_items.rb +0 -1
- data/lib/blather/stanza/iq.rb +8 -2
- data/lib/blather/stanza/iq/command.rb +18 -3
- data/lib/blather/stanza/iq/ibb.rb +6 -3
- data/lib/blather/stanza/iq/s5b.rb +6 -3
- data/lib/blather/stanza/iq/si.rb +6 -1
- data/lib/blather/stanza/iq/vcard.rb +3 -1
- data/lib/blather/stanza/message.rb +5 -0
- data/lib/blather/stanza/presence.rb +1 -0
- data/lib/blather/stanza/presence/c.rb +1 -0
- data/lib/blather/stanza/presence/status.rb +1 -0
- data/lib/blather/stanza/pubsub/event.rb +2 -4
- data/lib/blather/stanza/pubsub/subscription.rb +1 -0
- data/lib/blather/stanza/x.rb +8 -0
- data/lib/blather/stream.rb +2 -0
- data/lib/blather/stream/client.rb +1 -0
- data/lib/blather/stream/component.rb +1 -0
- data/lib/blather/stream/features.rb +4 -3
- data/lib/blather/stream/features/resource.rb +4 -3
- data/lib/blather/stream/features/sasl.rb +9 -6
- data/lib/blather/stream/features/session.rb +5 -4
- data/lib/blather/stream/features/tls.rb +4 -3
- data/lib/blather/stream/parser.rb +4 -5
- data/lib/blather/version.rb +2 -1
- data/lib/blather/xmpp_node.rb +9 -0
- data/spec/blather/client/client_spec.rb +14 -1
- data/spec/blather/stanza/iq_spec.rb +16 -0
- data/spec/blather/stanza/presence_spec.rb +1 -1
- data/spec/blather/stanza_spec.rb +18 -0
- data/spec/blather/stream/client_spec.rb +2 -2
- metadata +52 -35
- data/lib/blather/core_ext/active_support.rb +0 -45
- data/lib/blather/core_ext/active_support/inheritable_attributes.rb +0 -117
data/lib/blather/client/dsl.rb
CHANGED
@@ -217,8 +217,7 @@ module Blather
|
|
217
217
|
#
|
218
218
|
# Use this to stop the propogation of the stanza though the handler chain.
|
219
219
|
#
|
220
|
-
# @example
|
221
|
-
# Ignore all IQ stanzas
|
220
|
+
# @example Ignore all IQ stanzas
|
222
221
|
#
|
223
222
|
# before(:iq) { halt }
|
224
223
|
def halt
|
@@ -230,7 +229,8 @@ module Blather
|
|
230
229
|
# Use this to jump out of the current handler and let the next registered
|
231
230
|
# handler take care of the stanza
|
232
231
|
#
|
233
|
-
# @example
|
232
|
+
# @example Pass a message to the next handler
|
233
|
+
#
|
234
234
|
# This is contrive and should be handled with guards, but pass a message
|
235
235
|
# to the next handler based on the content
|
236
236
|
#
|
@@ -252,12 +252,18 @@ module Blather
|
|
252
252
|
client.write stanza
|
253
253
|
end
|
254
254
|
|
255
|
+
# Set the capabilities of the client
|
256
|
+
#
|
257
|
+
# @param [String] node the URI
|
258
|
+
# @param [Array<Hash>] identities an array of identities
|
259
|
+
# @param [Array<Hash>] features an array of features
|
255
260
|
def set_caps(node, identities, features)
|
256
261
|
client.caps.node = node
|
257
262
|
client.caps.identities = identities
|
258
263
|
client.caps.features = features
|
259
264
|
end
|
260
265
|
|
266
|
+
# Send capabilities to the server
|
261
267
|
def send_caps
|
262
268
|
client.register_handler :disco_info, :type => :get, :node => client.caps.node do |s|
|
263
269
|
r = client.caps.dup
|
@@ -1,4 +1,6 @@
|
|
1
|
+
# @private
|
1
2
|
module EventMachine
|
3
|
+
# @private
|
2
4
|
module Protocols
|
3
5
|
# Basic SOCKS v5 client implementation
|
4
6
|
#
|
@@ -16,6 +18,7 @@ module EventMachine
|
|
16
18
|
#
|
17
19
|
# EM.connect socks_host, socks_port, MyConn, host, port
|
18
20
|
#
|
21
|
+
# @private
|
19
22
|
class Socks5 < Connection
|
20
23
|
def initialize(host, port)
|
21
24
|
@host = host
|
@@ -119,4 +122,4 @@ module EventMachine
|
|
119
122
|
end
|
120
123
|
end
|
121
124
|
end
|
122
|
-
end
|
125
|
+
end
|
@@ -1,12 +1,14 @@
|
|
1
|
+
# @private
|
1
2
|
module Nokogiri
|
2
3
|
module XML
|
3
4
|
|
5
|
+
# Nokogiri::Node extensions
|
4
6
|
class Node
|
5
7
|
# Alias #name to #element_name so we can use #name in an XMPP Stanza context
|
6
8
|
alias_method :element_name, :name
|
7
9
|
alias_method :element_name=, :name=
|
8
10
|
|
9
|
-
alias_method :attr_set, :[]=
|
11
|
+
alias_method :attr_set, :[]=
|
10
12
|
# Override Nokogiri's attribute setter to add the ability to kill an attribute
|
11
13
|
# by setting it to nil and to be able to lookup an attribute by symbol
|
12
14
|
#
|
@@ -5,7 +5,9 @@ module Blather
|
|
5
5
|
#
|
6
6
|
# @handler :stanza_error
|
7
7
|
class StanzaError < BlatherError
|
8
|
+
# @private
|
8
9
|
STANZA_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-stanzas'
|
10
|
+
# @private
|
9
11
|
VALID_TYPES = [:cancel, :continue, :modify, :auth, :wait].freeze
|
10
12
|
|
11
13
|
register :stanza_error
|
@@ -105,4 +107,4 @@ class StanzaError < BlatherError
|
|
105
107
|
alias_method :to_s, :inspect
|
106
108
|
end # StanzaError
|
107
109
|
|
108
|
-
end # Blather
|
110
|
+
end # Blather
|
@@ -82,19 +82,22 @@ module Blather
|
|
82
82
|
@transferred = 0
|
83
83
|
end
|
84
84
|
|
85
|
+
# @private
|
85
86
|
def post_init
|
86
87
|
@file = File.open(@path, "w")
|
87
88
|
end
|
88
89
|
|
90
|
+
# @private
|
89
91
|
def receive_data(data)
|
90
92
|
@transferred += data.size
|
91
93
|
@file.write data
|
92
94
|
end
|
93
95
|
|
96
|
+
# @private
|
94
97
|
def unbind
|
95
98
|
@file.close
|
96
99
|
File.delete(@path) unless @transferred == @size
|
97
100
|
end
|
98
101
|
end
|
99
102
|
end
|
100
|
-
end
|
103
|
+
end
|
@@ -85,6 +85,7 @@ module Blather
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
# @private
|
88
89
|
class SocketConnection < EM::P::Socks5
|
89
90
|
include EM::Deferrable
|
90
91
|
|
@@ -96,9 +97,7 @@ module Blather
|
|
96
97
|
|
97
98
|
def post_init
|
98
99
|
self.succeed
|
99
|
-
class << self
|
100
|
-
include @@handler
|
101
|
-
end
|
100
|
+
(class << self; self; end).include(@@handler)
|
102
101
|
send(:initialize, *@params)
|
103
102
|
post_init
|
104
103
|
end
|
@@ -109,4 +108,4 @@ module Blather
|
|
109
108
|
end
|
110
109
|
end
|
111
110
|
end
|
112
|
-
end
|
111
|
+
end
|
data/lib/blather/jid.rb
CHANGED
@@ -46,12 +46,14 @@ module Blather
|
|
46
46
|
class JID
|
47
47
|
include Comparable
|
48
48
|
|
49
|
+
# Validating pattern for JID string
|
49
50
|
PATTERN = /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/.freeze
|
50
51
|
|
51
52
|
attr_reader :node,
|
52
53
|
:domain,
|
53
54
|
:resource
|
54
55
|
|
56
|
+
# @private
|
55
57
|
def self.new(node, domain = nil, resource = nil)
|
56
58
|
node.is_a?(JID) ? node : super
|
57
59
|
end
|
data/lib/blather/roster_item.rb
CHANGED
@@ -3,6 +3,7 @@ module Blather
|
|
3
3
|
# RosterItems hold internal representations of the user's roster
|
4
4
|
# including each JID's status.
|
5
5
|
class RosterItem
|
6
|
+
# @private
|
6
7
|
VALID_SUBSCRIPTION_TYPES = [:both, :from, :none, :remove, :to].freeze
|
7
8
|
|
8
9
|
attr_reader :jid,
|
@@ -12,6 +13,7 @@ module Blather
|
|
12
13
|
attr_accessor :name,
|
13
14
|
:groups
|
14
15
|
|
16
|
+
# @private
|
15
17
|
def self.new(item)
|
16
18
|
return item if item.is_a?(self)
|
17
19
|
super
|
@@ -118,8 +120,12 @@ module Blather
|
|
118
120
|
r
|
119
121
|
end
|
120
122
|
|
121
|
-
|
122
|
-
|
123
|
+
# Compare two RosterItems by their JID
|
124
|
+
#
|
125
|
+
# @param [Blather::JID] other the JID to compare against
|
126
|
+
# @return [Fixnum<-1, 0, 1>]
|
127
|
+
def <=>(other)
|
128
|
+
JID.new(self.jid) <=> JID.new(other.jid)
|
123
129
|
end
|
124
130
|
|
125
131
|
# Compare two RosterItem objects by name, type and category
|
@@ -130,7 +136,11 @@ module Blather
|
|
130
136
|
o.jid == self.jid &&
|
131
137
|
o.groups == self.groups
|
132
138
|
end
|
133
|
-
|
139
|
+
|
140
|
+
# @private
|
141
|
+
def ==(o)
|
142
|
+
eql?(o)
|
143
|
+
end
|
134
144
|
end #RosterItem
|
135
145
|
|
136
146
|
end
|
data/lib/blather/stanza.rb
CHANGED
@@ -54,16 +54,24 @@ module Blather
|
|
54
54
|
|
55
55
|
# Creates a copy with to and from swapped
|
56
56
|
#
|
57
|
+
# @param [Hash] opts options to pass to reply!
|
58
|
+
# @option opts [Boolean] :remove_children Wether or not to remove child nodes when replying
|
59
|
+
#
|
57
60
|
# @return [Blather::Stanza]
|
58
|
-
def reply
|
59
|
-
self.dup.reply!
|
61
|
+
def reply(opts = {})
|
62
|
+
self.dup.reply! opts
|
60
63
|
end
|
61
64
|
|
62
65
|
# Swaps from and to
|
63
66
|
#
|
67
|
+
# @param [Hash] opts Misc options
|
68
|
+
# @option opts [Boolean] :remove_children Wether or not to remove child nodes when replying
|
69
|
+
#
|
64
70
|
# @return [self]
|
65
|
-
def reply!
|
71
|
+
def reply!(opts = {})
|
72
|
+
opts = {:remove_children => false}.merge opts
|
66
73
|
self.to, self.from = self.from, self.to
|
74
|
+
self.children.remove if opts[:remove_children]
|
67
75
|
self
|
68
76
|
end
|
69
77
|
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
|
4
|
+
# # Capabilities Stanza
|
5
|
+
#
|
6
|
+
# [XEP-0115 Entity Capabilities](http://xmpp.org/extensions/xep-0115.html)
|
7
|
+
#
|
8
|
+
# XMPP protocol extension for broadcasting and dynamically discovering client, device, or generic entity capabilities.
|
9
|
+
#
|
10
|
+
class Capabilities < Blather::Stanza::DiscoInfo
|
11
|
+
def self.new
|
12
|
+
super :result
|
13
|
+
end
|
14
|
+
|
15
|
+
# A string that is used to verify the identity and supported features of the entity.
|
16
|
+
#
|
17
|
+
# @return [String]
|
18
|
+
def ver
|
19
|
+
generate_ver identities, features
|
20
|
+
end
|
21
|
+
|
22
|
+
# A URI that uniquely identifies a software application, typically a URL at the
|
23
|
+
# website of the project or company that produces the software.
|
24
|
+
#
|
25
|
+
# @param [String] node the node URI
|
26
|
+
def node=(node)
|
27
|
+
@bare_node = node
|
28
|
+
super "#{node}##{ver}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Add an array of identities
|
32
|
+
# @param identities the array of identities, passed directly to Identity.new
|
33
|
+
def identities=(identities)
|
34
|
+
super identities
|
35
|
+
regenerate_full_node
|
36
|
+
end
|
37
|
+
|
38
|
+
# Add an array of features
|
39
|
+
# @param features the array of features, passed directly to Feature.new
|
40
|
+
def features=(features)
|
41
|
+
super features
|
42
|
+
regenerate_full_node
|
43
|
+
end
|
44
|
+
|
45
|
+
# The generated Presence::C node
|
46
|
+
#
|
47
|
+
# @return [Blather::Stanza::Presence::C]
|
48
|
+
def c
|
49
|
+
Blather::Stanza::Presence::C.new @bare_node, ver
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def regenerate_full_node
|
55
|
+
self.node = @bare_node
|
56
|
+
end
|
57
|
+
|
58
|
+
def generate_ver_str(identities, features, forms = [])
|
59
|
+
# 1. Initialize an empty string S.
|
60
|
+
s = ''
|
61
|
+
|
62
|
+
# 2. Sort the service discovery identities by category and
|
63
|
+
# then by type (if it exists) and then by xml:lang (if it
|
64
|
+
# exists), formatted as CATEGORY '/' [TYPE] '/' [LANG] '/'
|
65
|
+
# [NAME]. Note that each slash is included even if the TYPE,
|
66
|
+
# LANG, or NAME is not included.
|
67
|
+
identities.sort! do |identity1, identity2|
|
68
|
+
cmp_result = nil
|
69
|
+
[:category, :type, :xml_lang, :name].each do |field|
|
70
|
+
value1 = identity1.send(field)
|
71
|
+
value2 = identity2.send(field)
|
72
|
+
|
73
|
+
if value1 != value2
|
74
|
+
cmp_result = value1 <=> value2
|
75
|
+
break
|
76
|
+
end
|
77
|
+
end
|
78
|
+
cmp_result
|
79
|
+
end
|
80
|
+
|
81
|
+
# 3. For each identity, append the 'category/type/lang/name' to
|
82
|
+
# S, followed by the '<' character.
|
83
|
+
s += identities.collect do |identity|
|
84
|
+
[:category, :type, :xml_lang, :name].collect do |field|
|
85
|
+
identity.send(field).to_s
|
86
|
+
end.join('/') + '<'
|
87
|
+
end.join
|
88
|
+
|
89
|
+
# 4. Sort the supported service discovery features.
|
90
|
+
features.sort! { |feature1, feature2| feature1.var <=> feature2.var }
|
91
|
+
|
92
|
+
# 5. For each feature, append the feature to S, followed by the
|
93
|
+
# '<' character.
|
94
|
+
s += features.collect { |feature| feature.var.to_s + '<' }.join
|
95
|
+
|
96
|
+
# 6. If the service discovery information response includes
|
97
|
+
# XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e., by
|
98
|
+
# the XML character data of the <value/> element).
|
99
|
+
forms.sort! do |form1, form2|
|
100
|
+
fform_type1 = form1.field 'FORM_TYPE'
|
101
|
+
fform_type2 = form2.field 'FORM_TYPE'
|
102
|
+
form_type1 = fform_type1 ? fform_type1.values.to_s : nil
|
103
|
+
form_type2 = fform_type2 ? fform_type2.values.to_s : nil
|
104
|
+
form_type1 <=> form_type2
|
105
|
+
end
|
106
|
+
|
107
|
+
# 7. For each extended service discovery information form:
|
108
|
+
forms.each do |form|
|
109
|
+
# 7.1. Append the XML character data of the FORM_TYPE field's
|
110
|
+
# <value/> element, followed by the '<' character.
|
111
|
+
fform_type = form.field 'FORM_TYPE'
|
112
|
+
form_type = fform_type ? fform_type.values.to_s : nil
|
113
|
+
s += "#{form_type}<"
|
114
|
+
|
115
|
+
# 7.2. Sort the fields by the value of the "var" attribute
|
116
|
+
fields = form.fields.sort { |field1, field2| field1.var <=> field2.var }
|
117
|
+
|
118
|
+
# 7.3. For each field:
|
119
|
+
fields.each do |field|
|
120
|
+
# 7.3.1. Append the value of the "var" attribute, followed by
|
121
|
+
# the '<' character.
|
122
|
+
s += "#{field.var}<"
|
123
|
+
|
124
|
+
# 7.3.2. Sort values by the XML character data of the <value/> element
|
125
|
+
# values = field.values.sort { |value1, value2| value1 <=> value2 }
|
126
|
+
|
127
|
+
# 7.3.3. For each <value/> element, append the XML character
|
128
|
+
# data, followed by the '<' character.
|
129
|
+
# s += values.collect { |value| "#{value}<" }.join
|
130
|
+
s += "#{field.value}<"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
s
|
134
|
+
end
|
135
|
+
|
136
|
+
def generate_ver(identities, features, forms = [], hash = 'sha-1')
|
137
|
+
s = generate_ver_str identities, features, forms
|
138
|
+
|
139
|
+
# 9. Compute the verification string by hashing S using the
|
140
|
+
# algorithm specified in the 'hash' attribute (e.g., SHA-1 as
|
141
|
+
# defined in RFC 3174). The hashed data MUST be generated
|
142
|
+
# with binary output and encoded using Base64 as specified in
|
143
|
+
# Section 4 of RFC 4648 (note: the Base64 output MUST NOT
|
144
|
+
# include whitespace and MUST set padding bits to zero).
|
145
|
+
|
146
|
+
# See http://www.iana.org/assignments/hash-function-text-names
|
147
|
+
hash_klass = case hash
|
148
|
+
when 'md2' then nil
|
149
|
+
when 'md5' then Digest::MD5
|
150
|
+
when 'sha-1' then Digest::SHA1
|
151
|
+
when 'sha-224' then nil
|
152
|
+
when 'sha-256' then Digest::SHA256
|
153
|
+
when 'sha-384' then Digest::SHA384
|
154
|
+
when 'sha-512' then Digest::SHA512
|
155
|
+
end
|
156
|
+
hash_klass ? [hash_klass::digest(s)].pack('m').strip : nil
|
157
|
+
end
|
158
|
+
end # Caps
|
159
|
+
|
160
|
+
end # Stanza
|
161
|
+
end # Blather
|
@@ -66,8 +66,9 @@ class Stanza
|
|
66
66
|
super o, *(fields + [:identities, :features])
|
67
67
|
end
|
68
68
|
|
69
|
+
# DiscoInfo::Identity
|
69
70
|
class Identity < XMPPNode
|
70
|
-
# Create a new DiscoInfo
|
71
|
+
# Create a new DiscoInfo::Identity
|
71
72
|
# @overload new(node)
|
72
73
|
# Imports the XML::Node to create a Identity object
|
73
74
|
# @param [XML::Node] node the node object to import
|
@@ -158,6 +159,7 @@ class Stanza
|
|
158
159
|
end
|
159
160
|
end # Identity
|
160
161
|
|
162
|
+
# DiscoInfo::Feature
|
161
163
|
class Feature < XMPPNode
|
162
164
|
# Create a new DiscoInfo::Feature object
|
163
165
|
# @overload new(node)
|