blather 0.4.14 → 0.4.15

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 (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
@@ -90,7 +90,7 @@ module Blather
90
90
  #
91
91
  # @param [Blather::Stanza::Status] the new status
92
92
  def status=(presence)
93
- @statuses.delete_if { |s| s.from == presence.from }
93
+ @statuses.delete_if { |s| s.from == presence.from || s.state == :unavailable }
94
94
  @statuses << presence
95
95
  @statuses.sort!
96
96
  end
@@ -102,7 +102,7 @@ module Blather
102
102
  top = if resource
103
103
  @statuses.detect { |s| s.from.resource == resource }
104
104
  else
105
- @statuses.first
105
+ @statuses.last
106
106
  end
107
107
  end
108
108
 
@@ -75,7 +75,7 @@ class Stanza
75
75
  # @param [String] name the name of the Identity
76
76
  # @param [String, nil] type the type of the Identity
77
77
  # @param [String, nil] category the category of the Identity
78
- def self.new(name, type = nil, category = nil)
78
+ def self.new(name, type = nil, category = nil, xml_lang = nil)
79
79
  new_node = super :identity
80
80
 
81
81
  case name
@@ -85,10 +85,12 @@ class Stanza
85
85
  new_node.name = name[:name]
86
86
  new_node.type = name[:type]
87
87
  new_node.category = name[:category]
88
+ new_node.xml_lang = name[:xml_lang]
88
89
  else
89
90
  new_node.name = name
90
91
  new_node.type = type
91
92
  new_node.category = category
93
+ new_node.xml_lang = xml_lang
92
94
  end
93
95
  new_node
94
96
  end
@@ -129,6 +131,18 @@ class Stanza
129
131
  write_attr :name, name
130
132
  end
131
133
 
134
+ # The Identity's xml_lang
135
+ # @return [String]
136
+ def xml_lang
137
+ read_attr "lang"
138
+ end
139
+
140
+ # Set the Identity's name
141
+ # @param [String] name the new name for the identity
142
+ def xml_lang=(xml_lang)
143
+ write_attr "xml:lang", xml_lang
144
+ end
145
+
132
146
  # Compare two Identity objects by name, type and category
133
147
  # @param [DiscoInfo::Identity] o the Identity object to compare against
134
148
  # @return [true, false]
@@ -139,7 +153,8 @@ class Stanza
139
153
 
140
154
  o.name == self.name &&
141
155
  o.type == self.type &&
142
- o.category == self.category
156
+ o.category == self.category &&
157
+ o.xml_lang == self.xml_lang
143
158
  end
144
159
  alias_method :==, :eql?
145
160
  end # Identity
@@ -0,0 +1,83 @@
1
+ module Blather
2
+ class Stanza
3
+ class Iq
4
+ NS_IBB = 'http://jabber.org/protocol/ibb'
5
+
6
+ # # In-Band Bytestreams Stanza
7
+ #
8
+ # [XEP-0047: In-Band Bytestreams](http://xmpp.org/extensions/xep-0047.html)
9
+ #
10
+ # @handler :ibb_open
11
+ # @handler :ibb_data
12
+ # @handler :ibb_close
13
+ class Ibb < Iq
14
+
15
+ # Overrides the parent method to remove open, close and data nodes
16
+ #
17
+ # @see Blather::Stanza#reply
18
+ def reply
19
+ reply = super
20
+ reply.remove_children :open
21
+ reply.remove_children :close
22
+ reply.remove_children :data
23
+ reply
24
+ end
25
+
26
+ class Open < Ibb
27
+ register :ibb_open, :open, NS_IBB
28
+
29
+ # Find open node
30
+ #
31
+ # @return [Nokogiri::XML::Element]
32
+ def open
33
+ find_first('ns:open', :ns => NS_IBB)
34
+ end
35
+
36
+ # Get the sid of the file transfer
37
+ #
38
+ # @return [String]
39
+ def sid
40
+ open['sid']
41
+ end
42
+
43
+ end
44
+
45
+ class Data < Ibb
46
+ register :ibb_data, :data, NS_IBB
47
+
48
+ # Find data node
49
+ #
50
+ # @return [Nokogiri::XML::Element]
51
+ def data
52
+ find_first('ns:data', :ns => NS_IBB)
53
+ end
54
+
55
+ # Get the sid of the file transfer
56
+ #
57
+ # @return [String]
58
+ def sid
59
+ data['sid']
60
+ end
61
+ end
62
+
63
+ class Close < Ibb
64
+ register :ibb_close, :close, NS_IBB
65
+
66
+ # Find close node
67
+ #
68
+ # @return [Nokogiri::XML::Element]
69
+ def close
70
+ find_first('ns:close', :ns => NS_IBB)
71
+ end
72
+
73
+ # Get the sid of the file transfer
74
+ #
75
+ # @return [String]
76
+ def sid
77
+ close['sid']
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,205 @@
1
+ module Blather
2
+ class Stanza
3
+ class Iq
4
+ NS_S5B = 'http://jabber.org/protocol/bytestreams'
5
+
6
+ # # SOCKS5 Bytestreams Stanza
7
+ #
8
+ # [XEP-0065: SOCKS5 Bytestreams](http://xmpp.org/extensions/xep-0065.html)
9
+ #
10
+ # @handler :s5b_open
11
+ class S5b < Query
12
+ register :s5b_open, :query, NS_S5B
13
+
14
+ # Overrides the parent method to remove query node
15
+ #
16
+ # @see Blather::Stanza#reply
17
+ def reply
18
+ reply = super
19
+ reply.remove_children :query
20
+ reply
21
+ end
22
+
23
+ # Get the sid of the file transfer
24
+ #
25
+ # @return [String]
26
+ def sid
27
+ query['sid']
28
+ end
29
+
30
+ # Get the used streamhost
31
+ #
32
+ # @return [S5b::StreamHostUsed]
33
+ def streamhost_used
34
+ StreamHostUsed.new query.find_first('.//ns:streamhost-used', :ns => self.class.registered_ns)
35
+ end
36
+
37
+ # Set the used streamhost
38
+ #
39
+ # @param [Blather::JID, String, nil] jid the jid of the used streamhost
40
+ def streamhost_used=(jid)
41
+ query.find('.//ns:streamhost-used', :ns => self.class.registered_ns).remove
42
+
43
+ if jid
44
+ query << StreamHostUsed.new(jid)
45
+ end
46
+ end
47
+
48
+ # Get the streamhosts
49
+ #
50
+ # @return [Array<S5b::StreamHost>]
51
+ def streamhosts
52
+ query.find('.//ns:streamhost', :ns => self.class.registered_ns).map do |s|
53
+ StreamHost.new s
54
+ end
55
+ end
56
+
57
+ # Set the streamhosts
58
+ #
59
+ # @param streamhosts the array of streamhosts, passed directly to StreamHost.new
60
+ def streamhosts=(streamhosts)
61
+ query.find('.//ns:streamhost', :ns => self.class.registered_ns).remove
62
+ if streamhosts
63
+ [streamhosts].flatten.each { |s| self.query << StreamHost.new(s) }
64
+ end
65
+ end
66
+
67
+ class StreamHost < XMPPNode
68
+ register 'streamhost', NS_S5B
69
+
70
+ # Create a new S5b::StreamHost
71
+ #
72
+ # @overload new(node)
73
+ # Create a new StreamHost by inheriting an existing node
74
+ # @param [XML::Node] node an XML::Node to inherit from
75
+ # @overload new(opts)
76
+ # Create a new StreamHost through a hash of options
77
+ # @param [Hash] opts a hash options
78
+ # @option opts [Blather::JID, String] :jid the JID of the StreamHost
79
+ # @option opts [#to_s] :host the host the StreamHost
80
+ # @option opts [#to_s] :port the post of the StreamHost
81
+ # @overload new(jid, host = nil, port = nil)
82
+ # Create a new StreamHost
83
+ # @param [Blather::JID, String] jid the JID of the StreamHost
84
+ # @param [#to_s] host the host the StreamHost
85
+ # @param [#to_s] port the post of the StreamHost
86
+ def self.new(jid, host = nil, port = nil)
87
+ new_node = super 'streamhost'
88
+
89
+ case jid
90
+ when Nokogiri::XML::Node
91
+ new_node.inherit jid
92
+ when Hash
93
+ new_node.jid = jid[:jid]
94
+ new_node.host = jid[:host]
95
+ new_node.port = jid[:port]
96
+ else
97
+ new_node.jid = jid
98
+ new_node.host = host
99
+ new_node.port = port
100
+ end
101
+ new_node
102
+ end
103
+
104
+ # Get the jid of the streamhost
105
+ #
106
+ # @return [Blather::JID, nil]
107
+ def jid
108
+ if j = read_attr(:jid)
109
+ JID.new(j)
110
+ else
111
+ nil
112
+ end
113
+ end
114
+
115
+ # Set the jid of the streamhost
116
+ #
117
+ # @param [Blather::JID, String, nil]
118
+ def jid=(j)
119
+ write_attr :jid, (j ? j.to_s : nil)
120
+ end
121
+
122
+ # Get the host address of the streamhost
123
+ #
124
+ # @return [String, nil]
125
+ def host
126
+ read_attr :host
127
+ end
128
+
129
+ # Set the host address of the streamhost
130
+ #
131
+ # @param [String, nil]
132
+ def host=(h)
133
+ write_attr :host, h
134
+ end
135
+
136
+ # Get the port of the streamhost
137
+ #
138
+ # @return [Fixnum, nil]
139
+ def port
140
+ if p = read_attr(:port)
141
+ p.to_i
142
+ else
143
+ nil
144
+ end
145
+ end
146
+
147
+ # Set the port of the streamhost
148
+ #
149
+ # @param [String, Fixnum, nil]
150
+ def port=(p)
151
+ write_attr :port, p
152
+ end
153
+ end
154
+
155
+ class StreamHostUsed < XMPPNode
156
+ register 'streamhost-used', NS_S5B
157
+
158
+ # Create a new S5b::StreamHostUsed
159
+ #
160
+ # @overload new(node)
161
+ # Create a new StreamHostUsed by inheriting an existing node
162
+ # @param [XML::Node] node an XML::Node to inherit from
163
+ # @overload new(opts)
164
+ # Create a new StreamHostUsed through a hash of options
165
+ # @param [Hash] opts a hash options
166
+ # @option opts [Blather::JID, String] :jid the JID of the StreamHostUsed
167
+ # @overload new(jid)
168
+ # Create a new StreamHostUsed
169
+ # @param [Blather::JID, String] jid the JID of the StreamHostUsed
170
+ def self.new(jid)
171
+ new_node = super 'streamhost-used'
172
+
173
+ case jid
174
+ when Nokogiri::XML::Node
175
+ new_node.inherit jid
176
+ when Hash
177
+ new_node.jid = jid[:jid]
178
+ else
179
+ new_node.jid = jid
180
+ end
181
+ new_node
182
+ end
183
+
184
+ # Get the jid of the used streamhost
185
+ #
186
+ # @return [Blather::JID, nil]
187
+ def jid
188
+ if j = read_attr(:jid)
189
+ JID.new(j)
190
+ else
191
+ nil
192
+ end
193
+ end
194
+
195
+ # Set the jid of the used streamhost
196
+ #
197
+ # @param [Blather::JID, String, nil]
198
+ def jid=(j)
199
+ write_attr :jid, (j ? j.to_s : nil)
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,410 @@
1
+ require 'time' # For Time#xmlschema
2
+
3
+ module Blather
4
+ class Stanza
5
+ class Iq
6
+
7
+ # # Si Stanza
8
+ #
9
+ # [XEP-0096: SI File Transfer](http://xmpp.org/extensions/xep-0096.html)
10
+ #
11
+ # This is a base class for any si based Iq stanzas. It provides a base set
12
+ # of methods for working with si stanzas
13
+ #
14
+ # @example Basic file transfer acceptance
15
+ # client.register_handler :file_transfer do |iq|
16
+ # transfer = Blather::FileTransfer.new(client, iq)
17
+ # transfer.accept(Blather::FileTransfer::SimpleFileReceiver, "/path/to/#{iq.si.file["name"]}", iq.si.file["size"].to_i)
18
+ # end
19
+ #
20
+ # @example Basic file transfer refusal
21
+ # client.register_handler :file_transfer do |iq|
22
+ # transfer = Blather::FileTransfer.new(client, iq)
23
+ # transfer.decline
24
+ # end
25
+ #
26
+ # @example File transfer acceptance by in-band bytestreams with custom handler
27
+ # client.register_handler :file_transfer do |iq|
28
+ # transfer = Blather::FileTransfer.new(client, iq)
29
+ # transfer.allow_ibb = true
30
+ # transfer.allow_s5b = false
31
+ # transfer.accept(MyFileReceiver, iq)
32
+ # end
33
+ #
34
+ # @handler :file_transfer
35
+ class Si < Iq
36
+ NS_SI = 'http://jabber.org/protocol/si'
37
+ register :file_transfer, :si, NS_SI
38
+
39
+ # Overrides the parent method to ensure a si node is created
40
+ #
41
+ # @see Blather::Stanza::Iq.new
42
+ def self.new(type = :set)
43
+ node = super
44
+ node.si
45
+ node
46
+ end
47
+
48
+ # Overrides the parent method to ensure the current si node is destroyed
49
+ #
50
+ # @see Blather::Stanza::Iq#inherit
51
+ def inherit(node)
52
+ si.remove
53
+ super
54
+ end
55
+
56
+ # Find or create si node
57
+ #
58
+ # @return [Si::Si]
59
+ def si
60
+ Si.find_or_create self
61
+ end
62
+
63
+ # Replaces si node
64
+ #
65
+ # @param [Si::Si, XML::Node] node the stanza's new si node
66
+ #
67
+ # @return [Si::Si]
68
+ def si=(node)
69
+ si.remove
70
+ self << node
71
+ Si.find_or_create self
72
+ end
73
+
74
+ # Overrides the parent method to ensure the current si node is destroyed
75
+ #
76
+ # @see Blather::Stanza#reply
77
+ def reply
78
+ reply = Stanza::Iq::Si.import super
79
+ reply.si.remove
80
+ reply
81
+ end
82
+
83
+ class Si < XMPPNode
84
+ # Create a new Si::Si object
85
+ #
86
+ # @param [XML::Node, nil] node a node to inherit from
87
+ #
88
+ # @return [Si::Si]
89
+ def self.new(node = nil)
90
+ new_node = super :si
91
+ new_node.namespace = NS_SI
92
+ new_node.inherit node if node
93
+ new_node
94
+ end
95
+
96
+ # Find or create si node in Si Iq and converts it to Si::Si
97
+ #
98
+ # @param [Si] parent a Si Iq where to find or create si
99
+ #
100
+ # @return [Si::Si]
101
+ def self.find_or_create(parent)
102
+ if found_si = parent.find_first('//ns:si', :ns => NS_SI)
103
+ si = self.new found_si
104
+ found_si.remove
105
+ else
106
+ si = self.new
107
+ end
108
+ parent << si
109
+
110
+ si
111
+ end
112
+
113
+ # Get the id of the stream
114
+ #
115
+ # @return [String, nil]
116
+ def id
117
+ read_attr :id
118
+ end
119
+
120
+ # Set the id
121
+ #
122
+ # @param [String, nil] id the id of the stream
123
+ def id=(id)
124
+ write_attr :id, id
125
+ end
126
+
127
+ # Get the MIME type of the stream
128
+ #
129
+ # @return [String, nil]
130
+ def mime_type
131
+ read_attr 'mime-type'
132
+ end
133
+
134
+ # Set the MIME type
135
+ #
136
+ # @param [String, nil] type the MIME type of the stream
137
+ def mime_type=(type)
138
+ write_attr 'mime-type', type
139
+ end
140
+
141
+ # Get the profile of the stream
142
+ #
143
+ # @return [String, nil]
144
+ def profile
145
+ read_attr :profile
146
+ end
147
+
148
+ # Set the profile
149
+ #
150
+ # @param [String, nil] profile the profile of the stream
151
+ def profile=(profile)
152
+ write_attr :profile, profile
153
+ end
154
+
155
+ # Find or create file node
156
+ #
157
+ # @return [Si::Si::File]
158
+ def file
159
+ File.find_or_create self
160
+ end
161
+
162
+ # Find or create feature node
163
+ #
164
+ # @return [Si::Si::Feature]
165
+ def feature
166
+ Feature.find_or_create self
167
+ end
168
+
169
+ class Feature < XMPPNode
170
+ register :feature, 'http://jabber.org/protocol/feature-neg'
171
+
172
+ # Create a new Si::Si::Feature object
173
+ #
174
+ # @param [XML::Node, nil] node a node to inherit from
175
+ #
176
+ # @return [Si::Si::Feature]
177
+ def self.new(node = nil)
178
+ new_node = super :feature
179
+ new_node.namespace = self.registered_ns
180
+ new_node.inherit node if node
181
+ new_node
182
+ end
183
+
184
+ # Find or create feature node in si node and converts it to Si::Si::Feature
185
+ #
186
+ # @param [Si::Si] parent a si node where to find or create feature
187
+ #
188
+ # @return [Si::Si::Feature]
189
+ def self.find_or_create(parent)
190
+ if found_feature = parent.find_first('//ns:feature', :ns => self.registered_ns)
191
+ feature = self.new found_feature
192
+ found_feature.remove
193
+ else
194
+ feature = self.new
195
+ end
196
+ parent << feature
197
+
198
+ feature
199
+ end
200
+
201
+ # Find or create x node
202
+ #
203
+ # @return [Stanza::X]
204
+ def x
205
+ Stanza::X.find_or_create self
206
+ end
207
+ end
208
+
209
+ class File < XMPPNode
210
+ register :file, 'http://jabber.org/protocol/si/profile/file-transfer'
211
+
212
+ # Create a new Si::Si::File object
213
+ #
214
+ # @param [XML::Node, nil] node a node to inherit from
215
+ #
216
+ # @return [Si::Si::File]
217
+ def self.new(name = nil, size = nil)
218
+ new_node = super :file
219
+
220
+ case name
221
+ when Nokogiri::XML::Node
222
+ new_node.inherit name
223
+ else
224
+ new_node.name = name
225
+ new_node.size = size
226
+ end
227
+ new_node
228
+ end
229
+
230
+ # Find or create file node in si node and converts it to Si::Si::File
231
+ #
232
+ # @param [Si::Si] parent a si node where to find or create file
233
+ #
234
+ # @return [Si::Si::File]
235
+ def self.find_or_create(parent)
236
+ if found_file = parent.find_first('//ns:file', :ns => self.registered_ns)
237
+ file = self.new found_file
238
+ found_file.remove
239
+ else
240
+ file = self.new
241
+ end
242
+ parent << file
243
+
244
+ file
245
+ end
246
+
247
+ # Get the filename
248
+ #
249
+ # @return [String, nil]
250
+ def name
251
+ read_attr :name
252
+ end
253
+
254
+ # Set the filename
255
+ #
256
+ # @param [String, nil] name the name of the file
257
+ def name=(name)
258
+ write_attr :name, name
259
+ end
260
+
261
+ # Get the hash
262
+ #
263
+ # @return [String, nil]
264
+ def hash
265
+ read_attr :hash
266
+ end
267
+
268
+ # Set the hash
269
+ #
270
+ # @param [String, nil] hash the MD5 hash of the file
271
+ def hash=(hash)
272
+ write_attr :hash, hash
273
+ end
274
+
275
+ # Get the date
276
+ #
277
+ # @return [Time, nil]
278
+ def date
279
+ begin
280
+ Time.xmlschema(read_attr(:date))
281
+ rescue ArgumentError
282
+ nil
283
+ end
284
+ end
285
+
286
+ # Set the date
287
+ #
288
+ # @param [Time, nil] date the last modification time of the file
289
+ def date=(date)
290
+ write_attr :date, (date ? date.xmlschema : nil)
291
+ end
292
+
293
+ # Get the size
294
+ #
295
+ # @return [Fixnum, nil]
296
+ def size
297
+ if (s = read_attr(:size)) && (s =~ /^\d+$/)
298
+ s.to_i
299
+ else
300
+ nil
301
+ end
302
+ end
303
+
304
+ # Set the size
305
+ #
306
+ # @param [Fixnum, nil] size the size, in bytes, of the file
307
+ def size=(size)
308
+ write_attr :size, size
309
+ end
310
+
311
+ # Get the desc
312
+ #
313
+ # @return [String, nil]
314
+ def desc
315
+ content_from 'ns:desc', :ns => self.class.registered_ns
316
+ end
317
+
318
+ # Set the desc
319
+ #
320
+ # @param [String, nil] desc the description of the file
321
+ def desc=(desc)
322
+ set_content_for :desc, desc
323
+ end
324
+
325
+ # Find or create range node
326
+ #
327
+ # @return [Si::Si::File::Range]
328
+ def range
329
+ Range.find_or_create self
330
+ end
331
+ end
332
+
333
+ class Range < XMPPNode
334
+ register :range, 'http://jabber.org/protocol/si/profile/file-transfer'
335
+
336
+ # Create a new Si::Si::File::Range object
337
+ #
338
+ # @param [XML::Node, nil] node a node to inherit from
339
+ #
340
+ # @return [Si::Si::File::Range]
341
+ def self.new(offset = nil, length = nil)
342
+ new_node = super :range
343
+
344
+ case offset
345
+ when Nokogiri::XML::Node
346
+ new_node.inherit offset
347
+ else
348
+ new_node.offset = offset
349
+ new_node.length = length
350
+ end
351
+ new_node
352
+ end
353
+ # Find or create range node in file node and converts it to Si::Si::File::Range
354
+ #
355
+ # @param [Si::Si::File] parent a file node where to find or create range
356
+ #
357
+ # @return [Si::Si::File::Range]
358
+ def self.find_or_create(parent)
359
+ if found_range = parent.find_first('//ns:range', :ns => self.registered_ns)
360
+ range = self.new found_range
361
+ found_range.remove
362
+ else
363
+ range = self.new
364
+ end
365
+ parent << range
366
+
367
+ range
368
+ end
369
+
370
+ # Get the offset
371
+ #
372
+ # @return [Fixnum, nil]
373
+ def offset
374
+ if (o = read_attr(:offset)) && (o =~ /^\d+$/)
375
+ o.to_i
376
+ else
377
+ nil
378
+ end
379
+ end
380
+
381
+ # Set the offset
382
+ #
383
+ # @param [Fixnum, nil] offset the position, in bytes, to start transferring the file data from
384
+ def offset=(offset)
385
+ write_attr :offset, offset
386
+ end
387
+
388
+ # Get the length
389
+ #
390
+ # @return [Fixnum, nil]
391
+ def length
392
+ if (l = read_attr(:length)) && (l =~ /^\d+$/)
393
+ l.to_i
394
+ else
395
+ nil
396
+ end
397
+ end
398
+
399
+ # Set the length
400
+ #
401
+ # @param [Fixnum, nil] length the number of bytes to retrieve starting at offset
402
+ def length=(length)
403
+ write_attr :length, length
404
+ end
405
+ end
406
+ end
407
+ end
408
+ end
409
+ end
410
+ end