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,428 @@
1
+ module Blather
2
+ class Stanza
3
+
4
+ # # Message Stanza
5
+ #
6
+ # [RFC 3921 Section 2.1 - Message Syntax](http://xmpp.org/rfcs/rfc3921.html#rfc.section.2.1)
7
+ #
8
+ # Exchanging messages is a basic use of XMPP and occurs when a user
9
+ # generates a message stanza that is addressed to another entity. The
10
+ # sender's server is responsible for delivering the message to the intended
11
+ # recipient (if the recipient is on the same local server) or for routing
12
+ # the message to the recipient's server (if the recipient is on a remote
13
+ # server). Thus a message stanza is used to "push" information to another
14
+ # entity.
15
+ #
16
+ # ## "To" Attribute
17
+ #
18
+ # An instant messaging client specifies an intended recipient for a message
19
+ # by providing the JID of an entity other than the sender in the `to`
20
+ # attribute of the Message stanza. If the message is being sent outside the
21
+ # context of any existing chat session or received message, the value of the
22
+ # `to` address SHOULD be of the form "user@domain" rather than of the form
23
+ # "user@domain/resource".
24
+ #
25
+ # msg = Message.new 'user@domain.tld/resource'
26
+ # msg.to == 'user@domain.tld/resource'
27
+ #
28
+ # msg.to = 'another-user@some-domain.tld/resource'
29
+ # msg.to == 'another-user@some-domain.tld/resource'
30
+ #
31
+ # The `to` attribute on a Message stanza works like any regular ruby object
32
+ # attribute
33
+ #
34
+ # ## "Type" Attribute
35
+ #
36
+ # Common uses of the message stanza in instant messaging applications
37
+ # include: single messages; messages sent in the context of a one-to-one
38
+ # chat session; messages sent in the context of a multi-user chat room;
39
+ # alerts, notifications, or other information to which no reply is expected;
40
+ # and errors. These uses are differentiated via the `type` attribute. If
41
+ # included, the `type` attribute MUST have one of the following values:
42
+ #
43
+ # * `:chat` -- The message is sent in the context of a one-to-one chat
44
+ # session. Typically a receiving client will present message of type
45
+ # `chat` in an interface that enables one-to-one chat between the two
46
+ # parties, including an appropriate conversation history.
47
+ #
48
+ # * `:error` -- The message is generated by an entity that experiences an
49
+ # error in processing a message received from another entity. A client
50
+ # that receives a message of type `error` SHOULD present an appropriate
51
+ # interface informing the sender of the nature of the error.
52
+ #
53
+ # * `:groupchat` -- The message is sent in the context of a multi-user chat
54
+ # environment (similar to that of [IRC]). Typically a receiving client
55
+ # will present a message of type `groupchat` in an interface that enables
56
+ # many-to-many chat between the parties, including a roster of parties in
57
+ # the chatroom and an appropriate conversation history.
58
+ #
59
+ # * `:headline` -- The message provides an alert, a notification, or other
60
+ # information to which no reply is expected (e.g., news headlines, sports
61
+ # updates, near-real-time market data, and syndicated content). Because no
62
+ # reply to the message is expected, typically a receiving client will
63
+ # present a message of type "headline" in an interface that appropriately
64
+ # differentiates the message from standalone messages, chat messages, or
65
+ # groupchat messages (e.g., by not providing the recipient with the
66
+ # ability to reply).
67
+ #
68
+ # * `:normal` -- The message is a standalone message that is sent outside
69
+ # the context of a one-to-one conversation or groupchat, and to which it
70
+ # is expected that the recipient will reply. Typically a receiving client
71
+ # will present a message of type `normal` in an interface that enables the
72
+ # recipient to reply, but without a conversation history. The default
73
+ # value of the `type` attribute is `normal`.
74
+ #
75
+ # Blather provides a helper for each possible type:
76
+ #
77
+ # Message#chat?
78
+ # Message#error?
79
+ # Message#groupchat?
80
+ # Message#headline?
81
+ # Message#normal?
82
+ #
83
+ # Blather treats the `type` attribute like a normal ruby object attribute
84
+ # providing a getter and setter. The default `type` is `chat`.
85
+ #
86
+ # msg = Message.new
87
+ # msg.type # => :chat
88
+ # msg.chat? # => true
89
+ # msg.type = :normal
90
+ # msg.normal? # => true
91
+ # msg.chat? # => false
92
+ #
93
+ # msg.type = :invalid # => RuntimeError
94
+ #
95
+ #
96
+ # ## "Body" Element
97
+ #
98
+ # The `body` element contains human-readable XML character data that
99
+ # specifies the textual contents of the message; this child element is
100
+ # normally included but is optional.
101
+ #
102
+ # Blather provides an attribute-like syntax for Message `body` elements.
103
+ #
104
+ # msg = Message.new 'user@domain.tld', 'message body'
105
+ # msg.body # => 'message body'
106
+ #
107
+ # msg.body = 'other message'
108
+ # msg.body # => 'other message'
109
+ #
110
+ # ## "Subject" Element
111
+ #
112
+ # The `subject` element contains human-readable XML character data that
113
+ # specifies the topic of the message.
114
+ #
115
+ # Blather provides an attribute-like syntax for Message `subject` elements.
116
+ #
117
+ # msg = Message.new 'user@domain.tld', 'message body'
118
+ # msg.subject = 'message subject'
119
+ # msg.subject # => 'message subject'
120
+ #
121
+ # ## "Thread" Element
122
+ #
123
+ # The primary use of the XMPP `thread` element is to uniquely identify a
124
+ # conversation thread or "chat session" between two entities instantiated by
125
+ # Message stanzas of type `chat`. However, the XMPP thread element can also
126
+ # be used to uniquely identify an analogous thread between two entities
127
+ # instantiated by Message stanzas of type `headline` or `normal`, or among
128
+ # multiple entities in the context of a multi-user chat room instantiated by
129
+ # Message stanzas of type `groupchat`. It MAY also be used for Message
130
+ # stanzas not related to a human conversation, such as a game session or an
131
+ # interaction between plugins. The `thread` element is not used to identify
132
+ # individual messages, only conversations or messagingg sessions. The
133
+ # inclusion of the `thread` element is optional.
134
+ #
135
+ # The value of the `thread` element is not human-readable and MUST be
136
+ # treated as opaque by entities; no semantic meaning can be derived from it,
137
+ # and only exact comparisons can be made against it. The value of the
138
+ # `thread` element MUST be a universally unique identifier (UUID) as
139
+ # described in [UUID].
140
+ #
141
+ # The `thread` element MAY possess a 'parent' attribute that identifies
142
+ # another thread of which the current thread is an offshoot or child; the
143
+ # value of the 'parent' must conform to the syntax of the `thread` element
144
+ # itself.
145
+ #
146
+ # Blather provides an attribute-like syntax for Message `thread` elements.
147
+ #
148
+ # msg = Message.new
149
+ # msg.thread = '12345'
150
+ # msg.thread # => '12345'
151
+ #
152
+ # Parent threads can be set using a hash:
153
+ #
154
+ # msg.thread = {'parent-id' => 'thread-id'}
155
+ # msg.thread # => 'thread-id'
156
+ # msg.parent_thread # => 'parent-id'
157
+ #
158
+ # @handler :message
159
+ class Message < Stanza
160
+ # @private
161
+ VALID_TYPES = [:chat, :error, :groupchat, :headline, :normal].freeze
162
+
163
+ # @private
164
+ VALID_CHAT_STATES = [:active, :composing, :gone, :inactive, :paused].freeze
165
+ # @private
166
+ CHAT_STATE_NS = 'http://jabber.org/protocol/chatstates'.freeze
167
+
168
+ # @private
169
+ HTML_NS = 'http://jabber.org/protocol/xhtml-im'.freeze
170
+ # @private
171
+ HTML_BODY_NS = 'http://www.w3.org/1999/xhtml'.freeze
172
+
173
+ register :message
174
+
175
+ # @private
176
+ def self.import(node)
177
+ klass = nil
178
+ node.children.detect do |e|
179
+ ns = e.namespace ? e.namespace.href : nil
180
+ klass = class_from_registration(e.element_name, ns)
181
+ end
182
+
183
+ if klass == Blather::Stanza::Presence::MUCUser
184
+ klass = Blather::Stanza::Message::MUCUser
185
+ end
186
+
187
+ if klass && klass != self && ![Blather::Stanza::X, Blather::Stanza::Iq].include?(klass)
188
+ klass.import(node)
189
+ else
190
+ new(node[:type]).inherit(node)
191
+ end
192
+ end
193
+
194
+ # Create a new Message stanza
195
+ #
196
+ # @param [#to_s] to the JID to send the message to
197
+ # @param [#to_s] body the body of the message
198
+ # @param [Symbol] type the message type. Must be one of VALID_TYPES
199
+ def self.new(to = nil, body = nil, type = :chat)
200
+ node = super :message
201
+ node.to = to
202
+ node.type = type
203
+ node.body = body
204
+ node.chat_state = :active if [:chat, :groupchat].include?(type)
205
+ node
206
+ end
207
+
208
+ # Overrides the parent method to ensure the current chat state is removed
209
+ #
210
+ # @see Blather::Stanza::Iq#inherit
211
+ def inherit(node)
212
+ xpath('ns:*', :ns => CHAT_STATE_NS).remove
213
+ super
214
+ end
215
+
216
+ # Check if the Message is of type :chat
217
+ #
218
+ # @return [true, false]
219
+ def chat?
220
+ self.type == :chat
221
+ end
222
+
223
+ # Check if the Message is of type :error
224
+ #
225
+ # @return [true, false]
226
+ def error?
227
+ self.type == :error
228
+ end
229
+
230
+ # Check if the Message is of type :groupchat
231
+ #
232
+ # @return [true, false]
233
+ def groupchat?
234
+ self.type == :groupchat
235
+ end
236
+
237
+ # Check if the Message is of type :headline
238
+ #
239
+ # @return [true, false]
240
+ def headline?
241
+ self.type == :headline
242
+ end
243
+
244
+ # Check if the Message is of type :normal
245
+ #
246
+ # @return [true, false]
247
+ def normal?
248
+ self.type == :normal
249
+ end
250
+
251
+ # Ensures type is :get, :set, :result or :error
252
+ #
253
+ # @param [#to_sym] type the Message type. Must be one of VALID_TYPES
254
+ def type=(type)
255
+ if type && !VALID_TYPES.include?(type.to_sym)
256
+ raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
257
+ end
258
+ super
259
+ end
260
+
261
+ # Get the message body
262
+ #
263
+ # @return [String]
264
+ def body
265
+ read_content :body
266
+ end
267
+
268
+ # Set the message body
269
+ #
270
+ # @param [#to_s] body the message body
271
+ def body=(body)
272
+ set_content_for :body, body
273
+ end
274
+
275
+ # Get the message xhtml node
276
+ # This will create the node if it doesn't exist
277
+ #
278
+ # @return [XML::Node]
279
+ def xhtml_node
280
+ unless h = find_first('ns:html', :ns => HTML_NS) || find_first('ns:html', :ns => HTML_BODY_NS)
281
+ self << (h = XMPPNode.new('html', self.document))
282
+ h.namespace = HTML_NS
283
+ end
284
+
285
+ unless b = h.find_first('ns:body', :ns => HTML_BODY_NS)
286
+ b = XMPPNode.new('body', self.document)
287
+ b.namespace = HTML_BODY_NS
288
+ h << b
289
+ end
290
+
291
+ b
292
+ end
293
+
294
+ # Get the message xhtml
295
+ #
296
+ # @return [String]
297
+ def xhtml
298
+ self.xhtml_node.inner_html.strip
299
+ end
300
+
301
+ # Set the message xhtml
302
+ # This will use Nokogiri to ensure the xhtml is valid
303
+ #
304
+ # @param [#to_s] valid xhtml
305
+ def xhtml=(xhtml_body)
306
+ self.xhtml_node.inner_html = Nokogiri::XML::DocumentFragment.parse(xhtml_body)
307
+ end
308
+
309
+ # Get the message subject
310
+ #
311
+ # @return [String]
312
+ def subject
313
+ read_content :subject
314
+ end
315
+
316
+ # Set the message subject
317
+ #
318
+ # @param [#to_s] body the message subject
319
+ def subject=(subject)
320
+ set_content_for :subject, subject
321
+ end
322
+
323
+ # Get the message thread
324
+ #
325
+ # @return [String]
326
+ def thread
327
+ read_content :thread
328
+ end
329
+
330
+ # Get the parent thread
331
+ #
332
+ # @return [String, nil]
333
+ def parent_thread
334
+ n = find_first('thread')
335
+ n[:parent] if n
336
+ end
337
+
338
+ # Set the thread
339
+ #
340
+ # @overload thread=(hash)
341
+ # Set a thread with a parent
342
+ # @param [Hash<parent-id => thread-id>] thread
343
+ # @overload thread=(thread)
344
+ # Set a thread id
345
+ # @param [#to_s] thread the new thread id
346
+ def thread=(thread)
347
+ parent, thread = thread.to_a.flatten if thread.is_a?(Hash)
348
+ set_content_for :thread, thread
349
+ find_first('thread')[:parent] = parent
350
+ end
351
+
352
+ # Returns the message's x:data form child
353
+ def form
354
+ X.find_or_create self
355
+ end
356
+
357
+ # Get the message chat state
358
+ #
359
+ # @return [Symbol]
360
+ def chat_state
361
+ if (elem = find_first('ns:*', :ns => CHAT_STATE_NS)) && VALID_CHAT_STATES.include?(name = elem.name.to_sym)
362
+ name
363
+ end
364
+ end
365
+
366
+ # Set the message chat state
367
+ #
368
+ # @param [#to_s] chat_state the message chat state. Must be one of VALID_CHAT_STATES
369
+ def chat_state=(chat_state)
370
+ if chat_state && !VALID_CHAT_STATES.include?(chat_state.to_sym)
371
+ raise ArgumentError, "Invalid Chat State (#{chat_state}), use: #{VALID_CHAT_STATES*' '}"
372
+ end
373
+
374
+ xpath('ns:*', :ns => CHAT_STATE_NS).remove
375
+
376
+ if chat_state
377
+ state = XMPPNode.new(chat_state, self.document)
378
+ state.namespace = CHAT_STATE_NS
379
+ self << state
380
+ end
381
+ end
382
+
383
+ def delay
384
+ if d = find_first('ns:delay', :ns => "urn:xmpp:delay")
385
+ Delay.new d
386
+ end
387
+ end
388
+
389
+ def delayed?
390
+ !!delay
391
+ end
392
+
393
+ class Delay < XMPPNode
394
+ def self.new(stamp = nil, from = nil, description = nil)
395
+ new_node = super :delay
396
+
397
+ case stamp
398
+ when Nokogiri::XML::Node
399
+ new_node.inherit stamp
400
+ when Hash
401
+ new_node.stamp = stamp[:stamp]
402
+ new_node.from = stamp[:from]
403
+ new_node.description = stamp[:description]
404
+ else
405
+ new_node.stamp = stamp
406
+ new_node.from = from
407
+ new_node.description = description
408
+ end
409
+ new_node
410
+ end
411
+
412
+ def from
413
+ read_attr :from
414
+ end
415
+
416
+ def stamp
417
+ s = read_attr :stamp
418
+ s && Time.parse(s)
419
+ end
420
+
421
+ def description
422
+ content.strip
423
+ end
424
+ end
425
+ end
426
+
427
+ end
428
+ end
@@ -0,0 +1,119 @@
1
+ require 'blather/stanza/muc/muc_user_base'
2
+
3
+ module Blather
4
+ class Stanza
5
+ class Message
6
+
7
+ class MUCUser < Message
8
+ include Blather::Stanza::MUC::MUCUserBase
9
+
10
+ register :muc_user_message, :x, "http://jabber.org/protocol/muc#user"
11
+
12
+ def self.new(to = nil, body = nil, type = :normal)
13
+ super
14
+ end
15
+
16
+ def invite?
17
+ !!find_invite_node
18
+ end
19
+
20
+ def invite_decline?
21
+ !!find_decline_node
22
+ end
23
+
24
+ def invite
25
+ if invite = find_invite_node
26
+ Invite.new invite
27
+ else
28
+ muc_user << (invite = Invite.new nil, nil, nil, self.document)
29
+ invite
30
+ end
31
+ end
32
+
33
+ def find_invite_node
34
+ muc_user.find_first 'ns:invite', :ns => self.class.registered_ns
35
+ end
36
+
37
+ def decline
38
+ if decline = find_decline_node
39
+ Decline.new decline
40
+ else
41
+ muc_user << (decline = Decline.new nil, nil, nil, self.document)
42
+ decline
43
+ end
44
+ end
45
+
46
+ def find_decline_node
47
+ muc_user.find_first 'ns:decline', :ns => self.class.registered_ns
48
+ end
49
+
50
+ class InviteBase < XMPPNode
51
+ def self.new(element_name, to = nil, from = nil, reason = nil, document = nil)
52
+ new_node = super element_name, document
53
+
54
+ case to
55
+ when self
56
+ to.document ||= document
57
+ return to
58
+ when Nokogiri::XML::Node
59
+ new_node.inherit to
60
+ when Hash
61
+ new_node.to = to[:to]
62
+ new_node.from = to[:from]
63
+ new_node.reason = to[:reason]
64
+ else
65
+ new_node.to = to
66
+ new_node.from = from
67
+ new_node.reason = reason
68
+ end
69
+ new_node
70
+ end
71
+
72
+ def to
73
+ read_attr :to
74
+ end
75
+
76
+ def to=(val)
77
+ write_attr :to, val
78
+ end
79
+
80
+ def from
81
+ read_attr :from
82
+ end
83
+
84
+ def from=(val)
85
+ write_attr :from, val
86
+ end
87
+
88
+ def reason
89
+ reason_node.content.strip
90
+ end
91
+
92
+ def reason=(val)
93
+ reason_node.content = val
94
+ end
95
+
96
+ def reason_node
97
+ unless reason = find_first('ns:reason', :ns => MUCUser.registered_ns)
98
+ self << (reason = XMPPNode.new('reason', self.document))
99
+ end
100
+ reason
101
+ end
102
+ end
103
+
104
+ class Invite < InviteBase
105
+ def self.new(*args)
106
+ new_node = super :invite, *args
107
+ end
108
+ end
109
+
110
+ class Decline < InviteBase
111
+ def self.new(*args)
112
+ new_node = super :decline, *args
113
+ end
114
+ end
115
+ end # MUC
116
+
117
+ end # Presence
118
+ end # Stanza
119
+ end # Blather