blather 0.4.14 → 0.4.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/lib/blather.rb +8 -0
  2. data/lib/blather/client/client.rb +9 -1
  3. data/lib/blather/client/dsl/pubsub.rb +2 -2
  4. data/lib/blather/core_ext/active_support.rb +1 -0
  5. data/lib/blather/core_ext/eventmachine.rb +122 -0
  6. data/lib/blather/errors/stanza_error.rb +1 -0
  7. data/lib/blather/file_transfer.rb +100 -0
  8. data/lib/blather/file_transfer/ibb.rb +68 -0
  9. data/lib/blather/file_transfer/s5b.rb +104 -0
  10. data/lib/blather/roster_item.rb +2 -2
  11. data/lib/blather/stanza/disco/disco_info.rb +17 -2
  12. data/lib/blather/stanza/iq/ibb.rb +83 -0
  13. data/lib/blather/stanza/iq/s5b.rb +205 -0
  14. data/lib/blather/stanza/iq/si.rb +410 -0
  15. data/lib/blather/stanza/iq/vcard.rb +147 -0
  16. data/lib/blather/stanza/message.rb +10 -2
  17. data/lib/blather/stanza/presence/status.rb +11 -3
  18. data/lib/blather/stanza/pubsub.rb +3 -1
  19. data/lib/blather/stanza/pubsub/event.rb +18 -0
  20. data/lib/blather/stanza/pubsub/subscriptions.rb +4 -2
  21. data/lib/blather/stanza/pubsub/unsubscribe.rb +17 -1
  22. data/lib/blather/stanza/x.rb +12 -2
  23. data/lib/blather/stream/parser.rb +1 -0
  24. data/lib/test.rb +55 -0
  25. data/spec/blather/client/client_spec.rb +18 -4
  26. data/spec/blather/client/dsl/pubsub_spec.rb +12 -2
  27. data/spec/blather/client/dsl_spec.rb +1 -1
  28. data/spec/blather/core_ext/nokogiri_spec.rb +1 -1
  29. data/spec/blather/errors/sasl_error_spec.rb +1 -1
  30. data/spec/blather/errors/stanza_error_spec.rb +1 -1
  31. data/spec/blather/errors/stream_error_spec.rb +1 -1
  32. data/spec/blather/errors_spec.rb +1 -1
  33. data/spec/blather/file_transfer_spec.rb +100 -0
  34. data/spec/blather/jid_spec.rb +1 -1
  35. data/spec/blather/roster_item_spec.rb +32 -2
  36. data/spec/blather/roster_spec.rb +1 -1
  37. data/spec/blather/stanza/discos/disco_info_spec.rb +12 -3
  38. data/spec/blather/stanza/discos/disco_items_spec.rb +1 -1
  39. data/spec/blather/stanza/iq/command_spec.rb +1 -1
  40. data/spec/blather/stanza/iq/ibb_spec.rb +136 -0
  41. data/spec/blather/stanza/iq/query_spec.rb +1 -1
  42. data/spec/blather/stanza/iq/roster_spec.rb +1 -1
  43. data/spec/blather/stanza/iq/s5b_spec.rb +60 -0
  44. data/spec/blather/stanza/iq/si_spec.rb +101 -0
  45. data/spec/blather/stanza/iq/vcard_spec.rb +96 -0
  46. data/spec/blather/stanza/iq_spec.rb +1 -1
  47. data/spec/blather/stanza/message_spec.rb +48 -2
  48. data/spec/blather/stanza/presence/status_spec.rb +1 -1
  49. data/spec/blather/stanza/presence/subscription_spec.rb +1 -1
  50. data/spec/blather/stanza/presence_spec.rb +1 -1
  51. data/spec/blather/stanza/pubsub/affiliations_spec.rb +2 -2
  52. data/spec/blather/stanza/pubsub/create_spec.rb +2 -2
  53. data/spec/blather/stanza/pubsub/event_spec.rb +16 -2
  54. data/spec/blather/stanza/pubsub/items_spec.rb +2 -2
  55. data/spec/blather/stanza/pubsub/publish_spec.rb +2 -2
  56. data/spec/blather/stanza/pubsub/retract_spec.rb +2 -2
  57. data/spec/blather/stanza/pubsub/subscribe_spec.rb +2 -2
  58. data/spec/blather/stanza/pubsub/subscription_spec.rb +2 -2
  59. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +3 -3
  60. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +15 -2
  61. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +2 -2
  62. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +2 -2
  63. data/spec/blather/stanza/pubsub_owner_spec.rb +2 -2
  64. data/spec/blather/stanza/pubsub_spec.rb +14 -2
  65. data/spec/blather/stanza/x_spec.rb +1 -1
  66. data/spec/blather/stanza_spec.rb +1 -1
  67. data/spec/blather/stream/client_spec.rb +1 -1
  68. data/spec/blather/stream/component_spec.rb +1 -1
  69. data/spec/blather/stream/parser_spec.rb +11 -1
  70. data/spec/blather/xmpp_node_spec.rb +1 -1
  71. data/spec/fixtures/pubsub.rb +2 -2
  72. data/spec/spec_helper.rb +27 -0
  73. metadata +85 -23
@@ -0,0 +1,147 @@
1
+ module Blather
2
+ class Stanza
3
+ class Iq
4
+
5
+ # # Vcard Stanza
6
+ #
7
+ # [XEP-0054 vcard-temp](http://xmpp.org/extensions/xep-0054.html)
8
+ #
9
+ # This is a base class for any vcard based Iq stanzas. It provides a base set
10
+ # of methods for working with vcard stanzas
11
+ #
12
+ # @example Retrieving One's vCard
13
+ # iq = Blather::Stanza::Iq::Vcard.new :get
14
+ # client.write_with_handler iq do |response|
15
+ # puts response.vcard
16
+ # end
17
+ #
18
+ # @example Updating One's vCard
19
+ # iq = Blather::Stanza::Iq::Vcard.new :set
20
+ # iq.vcard['NICKNAME'] = 'Romeo'
21
+ # client.write_with_handler iq do |response|
22
+ # puts response
23
+ # end
24
+ #
25
+ # @example Viewing Another User's vCard
26
+ # iq = Blather::Stanza::Iq::Vcard.new :get, 'mercutio@example.org'
27
+ # client.write_with_handler iq do |response|
28
+ # puts response.vcard
29
+ # end
30
+ #
31
+ # @handler :vcard
32
+ class Vcard < Iq
33
+
34
+ register :vcard, :vCard, 'vcard-temp'
35
+
36
+ # Overrides the parent method to ensure a vcard node is created
37
+ #
38
+ # @see Blather::Stanza::Iq.new
39
+ def self.new(type = nil, to = nil, id = nil)
40
+ node = super
41
+ node.vcard
42
+ node
43
+ end
44
+
45
+ # Overrides the parent method to ensure the current vcard node is destroyed
46
+ #
47
+ # @see Blather::Stanza::Iq#inherit
48
+ def inherit(node)
49
+ vcard.remove
50
+ super
51
+ self
52
+ end
53
+
54
+ # Find or create vcard node
55
+ #
56
+ # @return [Vcard::Vcard]
57
+ def vcard
58
+ Vcard.find_or_create self
59
+ end
60
+
61
+ # Replaces vcard node
62
+ #
63
+ # @param [Vcard::Vcard, XML::Node] info the stanza's new vcard node
64
+ #
65
+ # @return [Vcard::Vcard]
66
+ def vcard=(info)
67
+ vcard.remove
68
+ self << info
69
+ Vcard.find_or_create self
70
+ end
71
+
72
+ class Vcard < XMPPNode
73
+
74
+ VCARD_NS = 'vcard-temp'
75
+
76
+ # Create a new Vcard::Vcard object
77
+ #
78
+ # @param [XML::Node, nil] node a node to inherit from
79
+ #
80
+ # @return [Vcard::Vcard]
81
+ def self.new(node = nil)
82
+ new_node = super :vCard
83
+ new_node.namespace = VCARD_NS
84
+ new_node.inherit node if node
85
+ new_node
86
+ end
87
+
88
+ # Find or create vCard node in Vcard Iq and converts it to Vcard::Vcard
89
+ #
90
+ # @param [Vcard] parent a Vcard Iq where to find or create vCard
91
+ #
92
+ # @return [Vcard::Vcard]
93
+ def self.find_or_create(parent)
94
+ if found_vcard = parent.find_first('//ns:vCard', :ns => VCARD_NS)
95
+ vcard = self.new found_vcard
96
+ found_vcard.remove
97
+ else
98
+ vcard = self.new
99
+ end
100
+ parent << vcard
101
+
102
+ vcard
103
+ end
104
+
105
+ # Find the element's value by name
106
+ #
107
+ # @param [String] name the name of the element
108
+ #
109
+ # @return [String, nil]
110
+ def [](name)
111
+ name = name.split("/").map{|child| "ns:#{child}"}.join("/")
112
+
113
+ if elem = find_first(name, :ns => VCARD_NS)
114
+ elem.content
115
+ else
116
+ nil
117
+ end
118
+ end
119
+
120
+ # Set the element's value
121
+ #
122
+ # @param [String] name the name of the element
123
+ # @param [String, nil] value the new value of element
124
+ #
125
+ # @return [String, nil]
126
+ def []=(name, value)
127
+ elem = nil
128
+ parent = self
129
+
130
+ name.split("/").each do |child|
131
+ elem = parent.find_first("ns:#{child}", :ns => VCARD_NS)
132
+ unless elem
133
+ elem = XMPPNode.new(child, parent.document)
134
+ parent << elem
135
+ parent = elem
136
+ else
137
+ parent = elem
138
+ end
139
+ end
140
+
141
+ elem.content = value
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -175,7 +175,7 @@ class Stanza
175
175
  klass = class_from_registration(e.element_name, ns)
176
176
  end
177
177
 
178
- if klass && klass != self && klass != Blather::Stanza::X
178
+ if klass && klass != self && ![Blather::Stanza::X, Blather::Stanza::Iq].include?(klass)
179
179
  klass.import(node)
180
180
  else
181
181
  new(node[:type]).inherit(node)
@@ -196,6 +196,14 @@ class Stanza
196
196
  node
197
197
  end
198
198
 
199
+ # Overrides the parent method to ensure the current chat state is removed
200
+ #
201
+ # @see Blather::Stanza::Iq#inherit
202
+ def inherit(node)
203
+ xpath('ns:*', :ns => CHAT_STATE_NS).remove
204
+ super
205
+ end
206
+
199
207
  # Check if the Message is of type :chat
200
208
  #
201
209
  # @return [true, false]
@@ -260,7 +268,7 @@ class Stanza
260
268
  #
261
269
  # @return [XML::Node]
262
270
  def xhtml_node
263
- unless h = find_first('ns:html', :ns => HTML_NS)
271
+ unless h = find_first('ns:html', :ns => HTML_NS) || find_first('ns:html', :ns => HTML_BODY_NS)
264
272
  self << (h = XMPPNode.new('html', self.document))
265
273
  h.namespace = HTML_NS
266
274
  end
@@ -193,8 +193,9 @@ class Presence
193
193
  set_content_for :status, message
194
194
  end
195
195
 
196
- # Compare status based on priority
197
- # raises an error if the JIDs aren't the same
196
+ # Compare status based on priority and state:
197
+ # unavailable status is always less valuable than others
198
+ # Raises an error if the JIDs aren't the same
198
199
  #
199
200
  # @param [Blather::Stanza::Presence::Status] o
200
201
  # @return [true,false]
@@ -202,7 +203,14 @@ class Presence
202
203
  unless self.from && o.from && self.from.stripped == o.from.stripped
203
204
  raise ArgumentError, "Cannot compare status from different JIDs: #{[self.from, o.from].inspect}"
204
205
  end
205
- self.priority <=> o.priority
206
+
207
+ if (self.type.nil? && o.type.nil?) || (!self.type.nil? && !o.type.nil?)
208
+ self.priority <=> o.priority
209
+ elsif self.type.nil? && !o.type.nil?
210
+ 1
211
+ elsif !self.type.nil? && o.type.nil?
212
+ -1
213
+ end
206
214
  end
207
215
 
208
216
  end #Status
@@ -92,9 +92,11 @@ class Stanza
92
92
  write_attr :id, id
93
93
  end
94
94
 
95
+ alias_method :payload_node, :child
96
+
95
97
  # Get the item's payload
96
98
  #
97
- # @return [String, XMPPNode, nil]
99
+ # @return [String, nil]
98
100
  def payload
99
101
  children.empty? ? nil : children.to_s
100
102
  end
@@ -116,6 +116,24 @@ class PubSub
116
116
  n.content
117
117
  end
118
118
  end
119
+
120
+ # Check if this is a subscription stanza
121
+ #
122
+ # @return [XML::Node, nil]
123
+ def subscription?
124
+ subscription_node
125
+ end
126
+
127
+ # Get the actual subscription node
128
+ #
129
+ # @return [Blather::XMPPNode]
130
+ def subscription_node
131
+ event_node.find_first('//ns:subscription', :ns => self.class.registered_ns)
132
+ end
133
+
134
+ def subscription
135
+ subscription_node
136
+ end
119
137
  end # Event
120
138
 
121
139
  end # PubSub
@@ -58,7 +58,7 @@ class PubSub
58
58
  # Get a hash of subscriptions
59
59
  #
60
60
  # @example
61
- # { :subscribed => [{:node => 'node1', :jid => 'francisco@denmark.lit'}, {:node => 'node2', :jid => 'francisco@denmark.lit'}],
61
+ # { :subscribed => [{:node => 'node1', :jid => 'francisco@denmark.lit', :subid => 'fd8237yr872h3f289j2'}, {:node => 'node2', :jid => 'francisco@denmark.lit', :subid => 'h8394hf8923ju'}],
62
62
  # :unconfigured => [{:node => 'node3', :jid => 'francisco@denmark.lit'}],
63
63
  # :pending => [{:node => 'node4', :jid => 'francisco@denmark.lit'}],
64
64
  # :none => [{:node => 'node5', :jid => 'francisco@denmark.lit'}] }
@@ -67,10 +67,12 @@ class PubSub
67
67
  def list
68
68
  subscriptions.find('//ns:subscription', :ns => self.class.registered_ns).inject({}) do |hash, item|
69
69
  hash[item[:subscription].to_sym] ||= []
70
- hash[item[:subscription].to_sym] << {
70
+ sub = {
71
71
  :node => item[:node],
72
72
  :jid => item[:jid]
73
73
  }
74
+ sub[:subid] = item[:subid] if item[:subid]
75
+ hash[item[:subscription].to_sym] << sub
74
76
  hash
75
77
  end
76
78
  end
@@ -16,10 +16,12 @@ class PubSub
16
16
  # @param [String] host the host to send the request to
17
17
  # @param [String] node the node to unsubscribe from
18
18
  # @param [Blather::JID, #to_s] jid the JID of the unsubscription
19
- def self.new(type = :set, host = nil, node = nil, jid = nil)
19
+ # @param [String] subid the subscription ID of the unsubscription
20
+ def self.new(type = :set, host = nil, node = nil, jid = nil, subid = nil)
20
21
  new_node = super(type, host)
21
22
  new_node.node = node
22
23
  new_node.jid = jid
24
+ new_node.subid = subid
23
25
  new_node
24
26
  end
25
27
 
@@ -51,6 +53,20 @@ class PubSub
51
53
  unsubscribe[:node] = node
52
54
  end
53
55
 
56
+ # Get the subscription ID to unsubscribe from
57
+ #
58
+ # @return [String]
59
+ def subid
60
+ unsubscribe[:subid]
61
+ end
62
+
63
+ # Set the subscription ID to unsubscribe from
64
+ #
65
+ # @param [String] node
66
+ def subid=(subid)
67
+ unsubscribe[:subid] = subid
68
+ end
69
+
54
70
  # Get or create the actual unsubscribe node
55
71
  #
56
72
  # @return [Blather::XMPPNode]
@@ -150,6 +150,7 @@ class Stanza
150
150
  end
151
151
 
152
152
  class Field < XMPPNode
153
+ register :field, 'jabber:x:data'
153
154
  VALID_TYPES = [:boolean, :fixed, :hidden, :"jid-multi", :"jid-single", :"list-multi", :"list-single", :"text-multi", :"text-private", :"text-single"].freeze
154
155
 
155
156
  # Create a new X Field
@@ -292,7 +293,11 @@ class Stanza
292
293
  #
293
294
  # @param [true, false]
294
295
  def required?
295
- !self.find_first('required').nil?
296
+ if self.namespace
297
+ !self.find_first('ns:required', :ns => self.namespace.href).nil?
298
+ else
299
+ !self.find_first('required').nil?
300
+ end
296
301
  end
297
302
 
298
303
  # Set the field's required flag
@@ -307,7 +312,11 @@ class Stanza
307
312
  #
308
313
  # @return [Blather::Stanza::X::Field::Option]
309
314
  def options
310
- self.find(:option).map { |f| Option.new(f) }
315
+ if self.namespace
316
+ self.find('ns:option', :ns => self.namespace.href)
317
+ else
318
+ self.find('option')
319
+ end.map { |f| Option.new(f) }
311
320
  end
312
321
 
313
322
  # Add an array of options to field
@@ -329,6 +338,7 @@ class Stanza
329
338
  alias_method :==, :eql?
330
339
 
331
340
  class Option < XMPPNode
341
+ register :option, 'jabber:x:data'
332
342
  # Create a new X Field Option
333
343
  # @overload new(node)
334
344
  # Imports the XML::Node to create a Field option object
@@ -16,6 +16,7 @@ class Stream # :nodoc:
16
16
  @namespaces = {}
17
17
  @namespace_definitions = []
18
18
  @parser = Nokogiri::XML::SAX::PushParser.new self
19
+ @parser.options = Nokogiri::XML::ParseOptions::DEFAULT_XML | Nokogiri::XML::ParseOptions::NOENT
19
20
  end
20
21
 
21
22
  def receive_data(string)
data/lib/test.rb ADDED
@@ -0,0 +1,55 @@
1
+ require "blather/client/client"
2
+ require "openssl"
3
+ require "em-http"
4
+ require "cgi"
5
+
6
+ module S3FileReceiver
7
+ def initialize(iq)
8
+ p "initialize"
9
+ @filename = iq.si.file["name"]
10
+ @size = iq.si.file["size"].to_i
11
+ end
12
+
13
+ def post_init
14
+ p "post_init"
15
+ time = Time.now.httpdate
16
+ sign = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), "qHG5ywFiZR2EK7oUvLVLhGHr2Rp05ggriTdExw0I", "PUT\n\n\n#{time}\n/jaconda-dev/#{CGI::escape(@filename)}")).strip
17
+ @s3 = EM::HttpRequest.new("https://jaconda-dev.s3.amazonaws.com:443/#{CGI::escape(@filename)}").put({:timeout => 10, :head => {'date' => time, 'url' => "/jaconda-dev/#{CGI::escape(@filename)}", 'Content-Length' => @size, "Authorization" => "AWS AKIAIVULGEHUOR7PIR4Q:#{sign}"}})
18
+ @s3.errback do |e|
19
+ p "error: #{e.response_header.inspect}"
20
+ end
21
+ @s3.callback do |s|
22
+ p "success: #{s.response.inspect}"
23
+ end
24
+ EM::enable_proxy(self, @s3)
25
+ end
26
+
27
+ def proxy_target_unbound
28
+ close_connection_after_writing
29
+ end
30
+ end
31
+
32
+ EM.run do
33
+ Blather.logger = Logger.new("/Users/antonmironov/Desktop/blather.log")
34
+ Blather.logger.level = Logger::DEBUG
35
+
36
+ im = Blather::Client.setup "ant.mironov@jabber.ru/test", "zzzxxx"
37
+
38
+ im.register_handler :disco_info, :type => :get do |iq|
39
+ answer = iq.reply
40
+ answer.identities = [{:type => 'pc', :category => 'client'}]
41
+ answer.features = ["http://jabber.org/protocol/si/profile/file-transfer", "http://jabber.org/protocol/si", "http://jabber.org/protocol/bytestreams", "http://jabber.org/protocol/ibb"]
42
+
43
+ im.write answer
44
+ end
45
+
46
+ im.register_handler :file_transfer do |iq|
47
+ transfer = Blather::FileTransfer.new(im, iq)
48
+ # transfer.allow_bytestreams = false
49
+
50
+ transfer.accept(S3FileReceiver, iq)
51
+
52
+ true
53
+ end
54
+ im.connect
55
+ end
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
1
+ require File.expand_path "../../../spec_helper", __FILE__
2
2
  require 'blather/client/client'
3
3
 
4
4
  describe Blather::Client do
@@ -81,7 +81,7 @@ describe Blather::Client do
81
81
  disconnected = mock()
82
82
  disconnected.expects(:call)
83
83
  @client.register_handler(:disconnected) { disconnected.call }
84
- @client.unbind
84
+ @client.unbind
85
85
  end
86
86
 
87
87
  it 'does not call EM.stop on #unbind if a handler returns positive' do
@@ -175,6 +175,20 @@ describe Blather::Client do
175
175
  @client.register_handler(:iq) { |_| response.iq }
176
176
  @client.receive_data stanza
177
177
  end
178
+
179
+ it 'can clear handlers' do
180
+ stanza = Blather::Stanza::Message.new
181
+ stanza.expects(:chat?).returns true
182
+
183
+ response = mock
184
+ response.expects(:call).once
185
+
186
+ @client.register_handler(:message, :chat?) { |_| response.call }
187
+ @client.receive_data stanza
188
+
189
+ @client.clear_handlers(:message, :chat?)
190
+ @client.receive_data stanza
191
+ end
178
192
  end
179
193
 
180
194
  describe 'Blather::Client#write' do
@@ -309,7 +323,7 @@ describe 'Blather::Client with a Component stream' do
309
323
  class MockComponent < Blather::Stream::Component; def initialize(); end; end
310
324
  @stream = MockComponent.new('')
311
325
  @stream.stubs(:send_data)
312
- @client = Blather::Client.new
326
+ @client = Blather::Client.new
313
327
  @client.setup('me.com', 'secret')
314
328
  end
315
329
 
@@ -325,7 +339,7 @@ describe 'Blather::Client with a Client stream' do
325
339
  before do
326
340
  class MockClientStream < Blather::Stream::Client; def initialize(); end; end
327
341
  @stream = MockClientStream.new('')
328
- @client = Blather::Client.new
342
+ @client = Blather::Client.new
329
343
  Blather::Stream::Client.stubs(:start).returns @stream
330
344
  @client.setup('me@me.com', 'secret').run
331
345
  end