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.
Files changed (62) hide show
  1. data/.gemtest +0 -0
  2. data/CHANGELOG +12 -5
  3. data/README.md +1 -1
  4. data/Rakefile +1 -3
  5. data/blather.gemspec +11 -24
  6. data/examples/echo.rb +1 -0
  7. data/examples/execute.rb +1 -0
  8. data/examples/ping_pong.rb +1 -0
  9. data/examples/print_hierarchy.rb +1 -0
  10. data/examples/rosterprint.rb +1 -0
  11. data/examples/stream_only.rb +1 -0
  12. data/examples/xmpp4r/echo.rb +1 -0
  13. data/lib/blather.rb +5 -1
  14. data/lib/blather/client/client.rb +29 -154
  15. data/lib/blather/client/dsl.rb +9 -3
  16. data/lib/blather/client/dsl/pubsub.rb +2 -0
  17. data/lib/blather/core_ext/eventmachine.rb +4 -1
  18. data/lib/blather/core_ext/ipaddr.rb +2 -1
  19. data/lib/blather/core_ext/nokogiri.rb +3 -1
  20. data/lib/blather/errors/sasl_error.rb +1 -0
  21. data/lib/blather/errors/stanza_error.rb +3 -1
  22. data/lib/blather/errors/stream_error.rb +1 -0
  23. data/lib/blather/file_transfer.rb +4 -1
  24. data/lib/blather/file_transfer/s5b.rb +3 -4
  25. data/lib/blather/jid.rb +2 -0
  26. data/lib/blather/roster_item.rb +13 -3
  27. data/lib/blather/stanza.rb +11 -3
  28. data/lib/blather/stanza/disco/capabilities.rb +161 -0
  29. data/lib/blather/stanza/disco/disco_info.rb +3 -1
  30. data/lib/blather/stanza/disco/disco_items.rb +0 -1
  31. data/lib/blather/stanza/iq.rb +8 -2
  32. data/lib/blather/stanza/iq/command.rb +18 -3
  33. data/lib/blather/stanza/iq/ibb.rb +6 -3
  34. data/lib/blather/stanza/iq/s5b.rb +6 -3
  35. data/lib/blather/stanza/iq/si.rb +6 -1
  36. data/lib/blather/stanza/iq/vcard.rb +3 -1
  37. data/lib/blather/stanza/message.rb +5 -0
  38. data/lib/blather/stanza/presence.rb +1 -0
  39. data/lib/blather/stanza/presence/c.rb +1 -0
  40. data/lib/blather/stanza/presence/status.rb +1 -0
  41. data/lib/blather/stanza/pubsub/event.rb +2 -4
  42. data/lib/blather/stanza/pubsub/subscription.rb +1 -0
  43. data/lib/blather/stanza/x.rb +8 -0
  44. data/lib/blather/stream.rb +2 -0
  45. data/lib/blather/stream/client.rb +1 -0
  46. data/lib/blather/stream/component.rb +1 -0
  47. data/lib/blather/stream/features.rb +4 -3
  48. data/lib/blather/stream/features/resource.rb +4 -3
  49. data/lib/blather/stream/features/sasl.rb +9 -6
  50. data/lib/blather/stream/features/session.rb +5 -4
  51. data/lib/blather/stream/features/tls.rb +4 -3
  52. data/lib/blather/stream/parser.rb +4 -5
  53. data/lib/blather/version.rb +2 -1
  54. data/lib/blather/xmpp_node.rb +9 -0
  55. data/spec/blather/client/client_spec.rb +14 -1
  56. data/spec/blather/stanza/iq_spec.rb +16 -0
  57. data/spec/blather/stanza/presence_spec.rb +1 -1
  58. data/spec/blather/stanza_spec.rb +18 -0
  59. data/spec/blather/stream/client_spec.rb +2 -2
  60. metadata +52 -35
  61. data/lib/blather/core_ext/active_support.rb +0 -45
  62. data/lib/blather/core_ext/active_support/inheritable_attributes.rb +0 -117
@@ -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,6 +1,8 @@
1
1
  module Blather
2
2
  module DSL
3
3
 
4
+ # A helper class for providing a simplified PubSub interface to the
5
+ # DSL
4
6
  class PubSub
5
7
  attr_accessor :host
6
8
 
@@ -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,3 +1,4 @@
1
+ # @private
1
2
  class IPAddr
2
3
  PrivateRanges = [
3
4
  IPAddr.new("10.0.0.0/8"),
@@ -16,4 +17,4 @@ class IPAddr
16
17
  def public?
17
18
  !private?
18
19
  end
19
- end
20
+ 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, :[]= # :nodoc:
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,6 +5,7 @@ module Blather
5
5
  #
6
6
  # @handler :sasl_error
7
7
  class SASLError < BlatherError
8
+ # @private
8
9
  SASL_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-sasl'
9
10
 
10
11
  class_inheritable_accessor :err_name
@@ -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
@@ -5,6 +5,7 @@ module Blather
5
5
  #
6
6
  # @handler :stream_error
7
7
  class StreamError < BlatherError
8
+ # @private
8
9
  STREAM_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-streams'
9
10
 
10
11
  register :stream_error
@@ -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
@@ -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
- def <=>(o)
122
- self.jid.to_s <=> o.jid.to_s
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
- alias_method :==, :eql?
139
+
140
+ # @private
141
+ def ==(o)
142
+ eql?(o)
143
+ end
134
144
  end #RosterItem
135
145
 
136
146
  end
@@ -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 Identity
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)