tp-blather 0.8.2

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 (150) hide show
  1. data/.autotest +13 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +249 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +22 -0
  10. data/README.md +413 -0
  11. data/Rakefile +20 -0
  12. data/TODO.md +2 -0
  13. data/blather.gemspec +51 -0
  14. data/examples/certs/README +20 -0
  15. data/examples/certs/ca-bundle.crt +3987 -0
  16. data/examples/echo.rb +19 -0
  17. data/examples/execute.rb +17 -0
  18. data/examples/ping_pong.rb +38 -0
  19. data/examples/print_hierarchy.rb +77 -0
  20. data/examples/rosterprint.rb +15 -0
  21. data/examples/stream_only.rb +28 -0
  22. data/examples/trusted_echo.rb +21 -0
  23. data/examples/xmpp4r/echo.rb +36 -0
  24. data/lib/blather.rb +112 -0
  25. data/lib/blather/cert_store.rb +53 -0
  26. data/lib/blather/client.rb +95 -0
  27. data/lib/blather/client/client.rb +345 -0
  28. data/lib/blather/client/dsl.rb +320 -0
  29. data/lib/blather/client/dsl/pubsub.rb +174 -0
  30. data/lib/blather/core_ext/eventmachine.rb +125 -0
  31. data/lib/blather/core_ext/ipaddr.rb +20 -0
  32. data/lib/blather/errors.rb +69 -0
  33. data/lib/blather/errors/sasl_error.rb +44 -0
  34. data/lib/blather/errors/stanza_error.rb +110 -0
  35. data/lib/blather/errors/stream_error.rb +84 -0
  36. data/lib/blather/file_transfer.rb +107 -0
  37. data/lib/blather/file_transfer/ibb.rb +68 -0
  38. data/lib/blather/file_transfer/s5b.rb +114 -0
  39. data/lib/blather/jid.rb +141 -0
  40. data/lib/blather/roster.rb +118 -0
  41. data/lib/blather/roster_item.rb +146 -0
  42. data/lib/blather/stanza.rb +167 -0
  43. data/lib/blather/stanza/disco.rb +32 -0
  44. data/lib/blather/stanza/disco/capabilities.rb +161 -0
  45. data/lib/blather/stanza/disco/disco_info.rb +205 -0
  46. data/lib/blather/stanza/disco/disco_items.rb +134 -0
  47. data/lib/blather/stanza/iq.rb +144 -0
  48. data/lib/blather/stanza/iq/command.rb +339 -0
  49. data/lib/blather/stanza/iq/ibb.rb +86 -0
  50. data/lib/blather/stanza/iq/ping.rb +50 -0
  51. data/lib/blather/stanza/iq/query.rb +53 -0
  52. data/lib/blather/stanza/iq/roster.rb +185 -0
  53. data/lib/blather/stanza/iq/s5b.rb +208 -0
  54. data/lib/blather/stanza/iq/si.rb +415 -0
  55. data/lib/blather/stanza/iq/vcard.rb +149 -0
  56. data/lib/blather/stanza/message.rb +428 -0
  57. data/lib/blather/stanza/message/muc_user.rb +119 -0
  58. data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
  59. data/lib/blather/stanza/presence.rb +172 -0
  60. data/lib/blather/stanza/presence/c.rb +100 -0
  61. data/lib/blather/stanza/presence/muc.rb +35 -0
  62. data/lib/blather/stanza/presence/muc_user.rb +147 -0
  63. data/lib/blather/stanza/presence/status.rb +218 -0
  64. data/lib/blather/stanza/presence/subscription.rb +100 -0
  65. data/lib/blather/stanza/pubsub.rb +119 -0
  66. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  67. data/lib/blather/stanza/pubsub/create.rb +65 -0
  68. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  69. data/lib/blather/stanza/pubsub/event.rb +139 -0
  70. data/lib/blather/stanza/pubsub/items.rb +103 -0
  71. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  72. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  73. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  74. data/lib/blather/stanza/pubsub/subscription.rb +135 -0
  75. data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
  76. data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
  77. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  78. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  79. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  80. data/lib/blather/stanza/x.rb +416 -0
  81. data/lib/blather/stream.rb +266 -0
  82. data/lib/blather/stream/client.rb +32 -0
  83. data/lib/blather/stream/component.rb +39 -0
  84. data/lib/blather/stream/features.rb +70 -0
  85. data/lib/blather/stream/features/register.rb +38 -0
  86. data/lib/blather/stream/features/resource.rb +63 -0
  87. data/lib/blather/stream/features/sasl.rb +190 -0
  88. data/lib/blather/stream/features/session.rb +45 -0
  89. data/lib/blather/stream/features/tls.rb +29 -0
  90. data/lib/blather/stream/parser.rb +102 -0
  91. data/lib/blather/version.rb +3 -0
  92. data/lib/blather/xmpp_node.rb +94 -0
  93. data/spec/blather/client/client_spec.rb +687 -0
  94. data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
  95. data/spec/blather/client/dsl_spec.rb +266 -0
  96. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  97. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  98. data/spec/blather/errors/stream_error_spec.rb +108 -0
  99. data/spec/blather/errors_spec.rb +33 -0
  100. data/spec/blather/file_transfer_spec.rb +135 -0
  101. data/spec/blather/jid_spec.rb +87 -0
  102. data/spec/blather/roster_item_spec.rb +134 -0
  103. data/spec/blather/roster_spec.rb +107 -0
  104. data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
  105. data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
  106. data/spec/blather/stanza/iq/command_spec.rb +206 -0
  107. data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
  108. data/spec/blather/stanza/iq/ping_spec.rb +45 -0
  109. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  110. data/spec/blather/stanza/iq/roster_spec.rb +139 -0
  111. data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
  112. data/spec/blather/stanza/iq/si_spec.rb +98 -0
  113. data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
  114. data/spec/blather/stanza/iq_spec.rb +61 -0
  115. data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
  116. data/spec/blather/stanza/message_spec.rb +282 -0
  117. data/spec/blather/stanza/presence/c_spec.rb +56 -0
  118. data/spec/blather/stanza/presence/muc_spec.rb +37 -0
  119. data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
  120. data/spec/blather/stanza/presence/status_spec.rb +144 -0
  121. data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
  122. data/spec/blather/stanza/presence_spec.rb +125 -0
  123. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  124. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  125. data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
  126. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  127. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  128. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  129. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  130. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  131. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  132. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
  133. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  134. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  135. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  136. data/spec/blather/stanza/pubsub_spec.rb +68 -0
  137. data/spec/blather/stanza/x_spec.rb +231 -0
  138. data/spec/blather/stanza_spec.rb +134 -0
  139. data/spec/blather/stream/client_spec.rb +1090 -0
  140. data/spec/blather/stream/component_spec.rb +108 -0
  141. data/spec/blather/stream/parser_spec.rb +152 -0
  142. data/spec/blather/stream/ssl_spec.rb +32 -0
  143. data/spec/blather/xmpp_node_spec.rb +47 -0
  144. data/spec/blather_spec.rb +34 -0
  145. data/spec/fixtures/pubsub.rb +311 -0
  146. data/spec/spec_helper.rb +17 -0
  147. data/yard/templates/default/class/html/handlers.erb +18 -0
  148. data/yard/templates/default/class/setup.rb +10 -0
  149. data/yard/templates/default/class/text/handlers.erb +1 -0
  150. metadata +459 -0
@@ -0,0 +1,415 @@
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
+ # @private
37
+ NS_SI = 'http://jabber.org/protocol/si'
38
+ register :file_transfer, :si, NS_SI
39
+
40
+ # Overrides the parent method to ensure a si node is created
41
+ #
42
+ # @see Blather::Stanza::Iq.new
43
+ def self.new(type = :set)
44
+ node = super
45
+ node.si
46
+ node
47
+ end
48
+
49
+ # Overrides the parent method to ensure the current si node is destroyed
50
+ #
51
+ # @see Blather::Stanza::Iq#inherit
52
+ def inherit(node)
53
+ si.remove
54
+ super
55
+ end
56
+
57
+ # Find or create si node
58
+ #
59
+ # @return [Si::Si]
60
+ def si
61
+ Si.find_or_create self
62
+ end
63
+
64
+ # Replaces si node
65
+ #
66
+ # @param [Si::Si, XML::Node] node the stanza's new si node
67
+ #
68
+ # @return [Si::Si]
69
+ def si=(node)
70
+ si.remove
71
+ self << node
72
+ Si.find_or_create self
73
+ end
74
+
75
+ # Overrides the parent method to ensure the current si node is destroyed
76
+ #
77
+ # @see Blather::Stanza#reply
78
+ def reply
79
+ reply = Stanza::Iq::Si.import super
80
+ reply.si.remove
81
+ reply
82
+ end
83
+
84
+ # Si stanza fragment
85
+ class Si < XMPPNode
86
+ # Create a new Si::Si object
87
+ #
88
+ # @param [XML::Node, nil] node a node to inherit from
89
+ #
90
+ # @return [Si::Si]
91
+ def self.new(node = nil)
92
+ new_node = super :si
93
+ new_node.namespace = NS_SI
94
+ new_node.inherit node if node
95
+ new_node
96
+ end
97
+
98
+ # Find or create si node in Si Iq and converts it to Si::Si
99
+ #
100
+ # @param [Si] parent a Si Iq where to find or create si
101
+ #
102
+ # @return [Si::Si]
103
+ def self.find_or_create(parent)
104
+ if found_si = parent.find_first('//ns:si', :ns => NS_SI)
105
+ si = self.new found_si
106
+ found_si.remove
107
+ else
108
+ si = self.new
109
+ end
110
+ parent << si
111
+
112
+ si
113
+ end
114
+
115
+ # Get the id of the stream
116
+ #
117
+ # @return [String, nil]
118
+ def id
119
+ read_attr :id
120
+ end
121
+
122
+ # Set the id
123
+ #
124
+ # @param [String, nil] id the id of the stream
125
+ def id=(id)
126
+ write_attr :id, id
127
+ end
128
+
129
+ # Get the MIME type of the stream
130
+ #
131
+ # @return [String, nil]
132
+ def mime_type
133
+ read_attr 'mime-type'
134
+ end
135
+
136
+ # Set the MIME type
137
+ #
138
+ # @param [String, nil] type the MIME type of the stream
139
+ def mime_type=(type)
140
+ write_attr 'mime-type', type
141
+ end
142
+
143
+ # Get the profile of the stream
144
+ #
145
+ # @return [String, nil]
146
+ def profile
147
+ read_attr :profile
148
+ end
149
+
150
+ # Set the profile
151
+ #
152
+ # @param [String, nil] profile the profile of the stream
153
+ def profile=(profile)
154
+ write_attr :profile, profile
155
+ end
156
+
157
+ # Find or create file node
158
+ #
159
+ # @return [Si::Si::File]
160
+ def file
161
+ File.find_or_create self
162
+ end
163
+
164
+ # Find or create feature node
165
+ #
166
+ # @return [Si::Si::Feature]
167
+ def feature
168
+ Feature.find_or_create self
169
+ end
170
+
171
+ # Feature stanza fragment
172
+ class Feature < XMPPNode
173
+ register :feature, 'http://jabber.org/protocol/feature-neg'
174
+
175
+ # Create a new Si::Si::Feature object
176
+ #
177
+ # @param [XML::Node, nil] node a node to inherit from
178
+ #
179
+ # @return [Si::Si::Feature]
180
+ def self.new(node = nil)
181
+ new_node = super :feature
182
+ new_node.namespace = self.registered_ns
183
+ new_node.inherit node if node
184
+ new_node
185
+ end
186
+
187
+ # Find or create feature node in si node and converts it to Si::Si::Feature
188
+ #
189
+ # @param [Si::Si] parent a si node where to find or create feature
190
+ #
191
+ # @return [Si::Si::Feature]
192
+ def self.find_or_create(parent)
193
+ if found_feature = parent.find_first('//ns:feature', :ns => self.registered_ns)
194
+ feature = self.new found_feature
195
+ found_feature.remove
196
+ else
197
+ feature = self.new
198
+ end
199
+ parent << feature
200
+
201
+ feature
202
+ end
203
+
204
+ # Find or create x node
205
+ #
206
+ # @return [Stanza::X]
207
+ def x
208
+ Stanza::X.find_or_create self
209
+ end
210
+ end
211
+
212
+ # File stanza fragment
213
+ class File < XMPPNode
214
+ register :file, 'http://jabber.org/protocol/si/profile/file-transfer'
215
+
216
+ # Create a new Si::Si::File object
217
+ #
218
+ # @param [XML::Node, nil] node a node to inherit from
219
+ #
220
+ # @return [Si::Si::File]
221
+ def self.new(name = nil, size = nil)
222
+ new_node = super :file
223
+
224
+ case name
225
+ when Nokogiri::XML::Node
226
+ new_node.inherit name
227
+ else
228
+ new_node.name = name
229
+ new_node.size = size
230
+ end
231
+ new_node
232
+ end
233
+
234
+ # Find or create file node in si node and converts it to Si::Si::File
235
+ #
236
+ # @param [Si::Si] parent a si node where to find or create file
237
+ #
238
+ # @return [Si::Si::File]
239
+ def self.find_or_create(parent)
240
+ if found_file = parent.find_first('//ns:file', :ns => self.registered_ns)
241
+ file = self.new found_file
242
+ found_file.remove
243
+ else
244
+ file = self.new
245
+ end
246
+ parent << file
247
+
248
+ file
249
+ end
250
+
251
+ # Get the filename
252
+ #
253
+ # @return [String, nil]
254
+ def name
255
+ read_attr :name
256
+ end
257
+
258
+ # Set the filename
259
+ #
260
+ # @param [String, nil] name the name of the file
261
+ def name=(name)
262
+ write_attr :name, name
263
+ end
264
+
265
+ # Get the hash
266
+ #
267
+ # @return [String, nil]
268
+ def hash
269
+ read_attr :hash
270
+ end
271
+
272
+ # Set the hash
273
+ #
274
+ # @param [String, nil] hash the MD5 hash of the file
275
+ def hash=(hash)
276
+ write_attr :hash, hash
277
+ end
278
+
279
+ # Get the date
280
+ #
281
+ # @return [Time, nil]
282
+ def date
283
+ begin
284
+ Time.xmlschema(read_attr(:date))
285
+ rescue ArgumentError
286
+ nil
287
+ end
288
+ end
289
+
290
+ # Set the date
291
+ #
292
+ # @param [Time, nil] date the last modification time of the file
293
+ def date=(date)
294
+ write_attr :date, (date ? date.xmlschema : nil)
295
+ end
296
+
297
+ # Get the size
298
+ #
299
+ # @return [Fixnum, nil]
300
+ def size
301
+ if (s = read_attr(:size)) && (s =~ /^\d+$/)
302
+ s.to_i
303
+ else
304
+ nil
305
+ end
306
+ end
307
+
308
+ # Set the size
309
+ #
310
+ # @param [Fixnum, nil] size the size, in bytes, of the file
311
+ def size=(size)
312
+ write_attr :size, size
313
+ end
314
+
315
+ # Get the desc
316
+ #
317
+ # @return [String, nil]
318
+ def desc
319
+ content_from 'ns:desc', :ns => self.class.registered_ns
320
+ end
321
+
322
+ # Set the desc
323
+ #
324
+ # @param [String, nil] desc the description of the file
325
+ def desc=(desc)
326
+ set_content_for :desc, desc
327
+ end
328
+
329
+ # Find or create range node
330
+ #
331
+ # @return [Si::Si::File::Range]
332
+ def range
333
+ Range.find_or_create self
334
+ end
335
+ end
336
+
337
+ # Range stanza fragment
338
+ class Range < XMPPNode
339
+ register :range, 'http://jabber.org/protocol/si/profile/file-transfer'
340
+
341
+ # Create a new Si::Si::File::Range object
342
+ #
343
+ # @param [XML::Node, nil] node a node to inherit from
344
+ #
345
+ # @return [Si::Si::File::Range]
346
+ def self.new(offset = nil, length = nil)
347
+ new_node = super :range
348
+
349
+ case offset
350
+ when Nokogiri::XML::Node
351
+ new_node.inherit offset
352
+ else
353
+ new_node.offset = offset
354
+ new_node.length = length
355
+ end
356
+ new_node
357
+ end
358
+ # Find or create range node in file node and converts it to Si::Si::File::Range
359
+ #
360
+ # @param [Si::Si::File] parent a file node where to find or create range
361
+ #
362
+ # @return [Si::Si::File::Range]
363
+ def self.find_or_create(parent)
364
+ if found_range = parent.find_first('//ns:range', :ns => self.registered_ns)
365
+ range = self.new found_range
366
+ found_range.remove
367
+ else
368
+ range = self.new
369
+ end
370
+ parent << range
371
+
372
+ range
373
+ end
374
+
375
+ # Get the offset
376
+ #
377
+ # @return [Fixnum, nil]
378
+ def offset
379
+ if (o = read_attr(:offset)) && (o =~ /^\d+$/)
380
+ o.to_i
381
+ else
382
+ nil
383
+ end
384
+ end
385
+
386
+ # Set the offset
387
+ #
388
+ # @param [Fixnum, nil] offset the position, in bytes, to start transferring the file data from
389
+ def offset=(offset)
390
+ write_attr :offset, offset
391
+ end
392
+
393
+ # Get the length
394
+ #
395
+ # @return [Fixnum, nil]
396
+ def length
397
+ if (l = read_attr(:length)) && (l =~ /^\d+$/)
398
+ l.to_i
399
+ else
400
+ nil
401
+ end
402
+ end
403
+
404
+ # Set the length
405
+ #
406
+ # @param [Fixnum, nil] length the number of bytes to retrieve starting at offset
407
+ def length=(length)
408
+ write_attr :length, length
409
+ end
410
+ end
411
+ end
412
+ end
413
+ end
414
+ end
415
+ end
@@ -0,0 +1,149 @@
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
+ # Vcard stanza fragment
73
+ class Vcard < XMPPNode
74
+
75
+ # @private
76
+ VCARD_NS = 'vcard-temp'
77
+
78
+ # Create a new Vcard::Vcard object
79
+ #
80
+ # @param [XML::Node, nil] node a node to inherit from
81
+ #
82
+ # @return [Vcard::Vcard]
83
+ def self.new(node = nil)
84
+ new_node = super :vCard
85
+ new_node.namespace = VCARD_NS
86
+ new_node.inherit node if node
87
+ new_node
88
+ end
89
+
90
+ # Find or create vCard node in Vcard Iq and converts it to Vcard::Vcard
91
+ #
92
+ # @param [Vcard] parent a Vcard Iq where to find or create vCard
93
+ #
94
+ # @return [Vcard::Vcard]
95
+ def self.find_or_create(parent)
96
+ if found_vcard = parent.find_first('//ns:vCard', :ns => VCARD_NS)
97
+ vcard = self.new found_vcard
98
+ found_vcard.remove
99
+ else
100
+ vcard = self.new
101
+ end
102
+ parent << vcard
103
+
104
+ vcard
105
+ end
106
+
107
+ # Find the element's value by name
108
+ #
109
+ # @param [String] name the name of the element
110
+ #
111
+ # @return [String, nil]
112
+ def [](name)
113
+ name = name.split("/").map{|child| "ns:#{child}"}.join("/")
114
+
115
+ if elem = find_first(name, :ns => VCARD_NS)
116
+ elem.content
117
+ else
118
+ nil
119
+ end
120
+ end
121
+
122
+ # Set the element's value
123
+ #
124
+ # @param [String] name the name of the element
125
+ # @param [String, nil] value the new value of element
126
+ #
127
+ # @return [String, nil]
128
+ def []=(name, value)
129
+ elem = nil
130
+ parent = self
131
+
132
+ name.split("/").each do |child|
133
+ elem = parent.find_first("ns:#{child}", :ns => VCARD_NS)
134
+ unless elem
135
+ elem = XMPPNode.new(child, parent.document)
136
+ parent << elem
137
+ parent = elem
138
+ else
139
+ parent = elem
140
+ end
141
+ end
142
+
143
+ elem.content = value
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end