blather 0.4.16 → 0.5.0

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