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