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
@@ -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