em-xmpp 0.0.10 → 0.0.11

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.
@@ -0,0 +1,105 @@
1
+
2
+ require 'fiber'
3
+ module EM::Xmpp
4
+ class Conversation
5
+
6
+ class CallbackSet
7
+ def initialize
8
+ @callbacks = {}
9
+ end
10
+ def on(key,&blk)
11
+ @callbacks[key] = blk
12
+ end
13
+ def cb(key)
14
+ @callbacks[key]
15
+ end
16
+ end
17
+
18
+ Continue = Struct.new(:type, :ctx) do
19
+ def timeout?
20
+ type == :timeout
21
+ end
22
+ def interrupted?
23
+ not timeout?
24
+ end
25
+ end
26
+ Timeout = Continue.new(:timeout, nil)
27
+
28
+ def self.start(ctx,state)
29
+ fib = Fiber.new do
30
+ conv = Fiber.yield
31
+ yield conv
32
+ end
33
+ fib.resume #first resume starts the fiber
34
+ obj = self.new(ctx,state,fib)
35
+ fib.resume obj #second resume injects the conversation to call block
36
+ obj
37
+ end
38
+
39
+ # A list of callbacks
40
+ attr_reader :callbacks
41
+
42
+ # A user-provided state
43
+ attr_reader :state
44
+
45
+ def initialize(ctx,state,fiber=Fiber.current)
46
+ @connection = ctx.connection
47
+ @fiber = fiber
48
+ @state = state
49
+ @callbacks = CallbackSet.new
50
+ end
51
+
52
+ def prepare_callbacks(*keys)
53
+ yield callbacks
54
+ expect_callbacks *keys
55
+ end
56
+
57
+ private
58
+ def expect_callbacks(*keys)
59
+ missing = keys.reject {|k| callbacks.cb(k) }.map(&:to_s)
60
+ raise RuntimeError, "missing callbacks #{missing.join(' ')}" unless missing.empty?
61
+ end
62
+ public
63
+
64
+ def callback(key,&blk)
65
+ cb = callbacks.cb(key)
66
+ raise RuntimeError, "no such callback #{key}" unless cb
67
+ cb.call state, &blk
68
+ end
69
+
70
+ def start_timeout(seconds=:forever)
71
+ timer = nil
72
+ unless seconds == :forever
73
+ timer = EM::Timer.new(seconds) do
74
+ wake_up Timeout if @fiber
75
+ end
76
+ end
77
+ timer
78
+ end
79
+
80
+ def delay(seconds=:forever)
81
+ timer = start_timeout seconds
82
+ ret = Fiber.yield
83
+ timer.cancel if timer
84
+ ret
85
+ end
86
+
87
+ def resume(ctx)
88
+ wake_up Continue.new(:resumed, ctx)
89
+ end
90
+
91
+ def wake_up(obj)
92
+ @fiber.resume obj
93
+ end
94
+
95
+ def send_stanza(stanza,seconds=:forever)
96
+ @connection.send_stanza(stanza) do |response|
97
+ resume response
98
+ end
99
+ timer = start_timeout seconds if seconds
100
+ ret = Fiber.yield unless seconds == :no_response
101
+ timer.cancel if timer
102
+ ret
103
+ end
104
+ end
105
+ end
@@ -14,43 +14,73 @@ module EM::Xmpp
14
14
  yield self if block_given?
15
15
  end
16
16
 
17
+ # returns the domain entity of this entity
17
18
  def domain
18
19
  Entity.new(connection, jid.domain)
19
20
  end
20
21
 
22
+ # returns the bare entity of this entity
21
23
  def bare
22
24
  Entity.new(connection, jid.bare)
23
25
  end
24
26
 
27
+ # returns the full jid of this entity
25
28
  def full
26
29
  jid.full
27
30
  end
28
31
 
32
+ # to_s is defined as the jid.to_s
33
+ # so that you can just pass the entity when building stanzas
34
+ # to refer to the entity (e.g., message_stanza('to' => some_entity) )
29
35
  def to_s
30
36
  jid.to_s
31
37
  end
32
38
 
33
- #TODO: pub, sub, etc.
34
-
35
- def say(body)
36
- msg = connection.message_stanza(:to => jid) do |x|
37
- x.body body
38
- end
39
- connection.send_stanza msg
39
+ # sends a subscription request to the bare entity
40
+ def subscribe(&blk)
41
+ pres = connection.presence_stanza('to'=>jid.bare, 'type' => 'subscribe')
42
+ connection.send_stanza pres, &blk
40
43
  end
41
44
 
42
- def subscribe
43
- pres = connection.presence_stanza('to'=>jid.bare, 'type' => 'subscribe')
44
- connection.send_stanza pres
45
+ # send a subscription stanza to accept an incoming subscription request
46
+ def accept_subscription(&blk)
47
+ pres = connection.presence_stanza('to'=>jid.bare, 'type' => 'subscribed')
48
+ connection.send_stanza pres, &blk
45
49
  end
46
50
 
47
- def unsubscribe
51
+ # unsubscribes from from the bare entity
52
+ def unsubscribe(&blk)
48
53
  pres = connection.presence_stanza('to'=>jid.bare, 'type' => 'unsubscribe')
49
- connection.send_stanza pres
54
+ connection.send_stanza pres, &blk
50
55
  end
51
56
 
52
- def add_to_roster(display_name=nil,groups=[])
57
+ # sends some plain message to the entity (use type = 'chat')
58
+ def say(body, type='chat', xmlproc=nil, &blk)
59
+ msg = connection.message_stanza(:to => jid, :type => type) do |xml|
60
+ xml.body body
61
+ xmlproc.call xml if xmlproc
62
+ end
63
+ connection.send_stanza msg, &blk
64
+ end
65
+
66
+ private
67
+
68
+ def send_iq_stanza_fibered(iq)
53
69
  f = Fiber.current
70
+ connection.send_stanza(iq) do |ctx|
71
+ f.resume ctx
72
+ end
73
+ Fiber.yield
74
+ end
75
+
76
+ public
77
+
78
+ # add the entity (bare) to the roster
79
+ # optional parameters can set the display name (or friendly name)
80
+ # for the roster entity
81
+ #
82
+ # similarly, you can attach the entity to one or multiple groups
83
+ def add_to_roster(display_name=nil,groups=[])
54
84
  item_fields = {:jid => jid.bare}
55
85
  item_fields[:name] = display_name if display_name
56
86
 
@@ -63,14 +93,11 @@ module EM::Xmpp
63
93
  end
64
94
  end
65
95
 
66
- connection.send_stanza(query) do |ctx|
67
- f.resume ctx
68
- end
69
- Fiber.yield
96
+ send_iq_stanza_fibered query
70
97
  end
71
98
 
99
+ # removes an entity (bare) from the roster
72
100
  def remove_from_roster
73
- f = Fiber.current
74
101
  item_fields = {:jid => jid.bare, :subscription => 'remove'}
75
102
 
76
103
  query = connection.iq_stanza(:type => 'set') do |iq|
@@ -78,34 +105,735 @@ module EM::Xmpp
78
105
  q.item item_fields
79
106
  end
80
107
  end
81
- connection.send_stanza(query) do |ctx|
82
- f.resume ctx
83
- end
84
- Fiber.yield
108
+
109
+ send_iq_stanza_fibered query
85
110
  end
86
111
 
112
+ # discovers infos (disco#infos) about an entity
113
+ # can optionally specify a node of the entity
87
114
  def discover_infos(node=nil)
88
- f = Fiber.current
89
115
  hash = {'xmlns' => Namespaces::DiscoverInfos}
90
116
  hash['node'] = node if node
91
117
  iq = connection.iq_stanza('to'=>jid) do |xml|
92
118
  xml.query(hash)
93
119
  end
94
- connection.send_stanza(iq) do |ctx|
95
- f.resume ctx
96
- end
97
- Fiber.yield
120
+ send_iq_stanza_fibered iq
98
121
  end
99
122
 
123
+ # discovers items (disco#items) of an entity
124
+ # can optionally specify a node to discover
100
125
  def discover_items(node=nil)
101
- f = Fiber.current
102
126
  iq = connection.iq_stanza('to'=>jid.to_s) do |xml|
103
127
  xml.query('xmlns' => Namespaces::DiscoverItems)
104
128
  end
105
- connection.send_stanza(iq) do |ctx|
106
- f.resume ctx
129
+ send_iq_stanza_fibered iq
130
+ end
131
+
132
+ # returns a PubSub entity with same bare jid
133
+ # accepts an optional node-id
134
+ def pubsub(nid=nil)
135
+ node_jid = if nid
136
+ JID.new(jid.node, jid.domain, nid)
137
+ else
138
+ jid.to_s
139
+ end
140
+ PubSub.new(connection, node_jid)
141
+ end
142
+
143
+ # returns a (file-)Transfer entity with same jid
144
+ def transfer
145
+ Transfer.new(connection, jid)
146
+ end
147
+
148
+ # returns an entity to communicate with the Avatar service
149
+ def avatar
150
+ Avatar.new(connection, jid.bare)
151
+ end
152
+
153
+ class Transfer < Entity
154
+ def self.describe_file(path)
155
+ ret = {}
156
+ ret[:name] = File.basename path
157
+ ret[:size] = File.read(path).size #FIXME use file stats
158
+ ret[:mime] = 'text/plain' #FIXME
159
+ ret[:hash] = nil #TODO
160
+ ret[:date] = nil #TODO
161
+ ret
107
162
  end
108
- Fiber.yield
163
+
164
+ def negotiation_request(filedesc,sid,form)
165
+ si_args = {'profile' => EM::Xmpp::Namespaces::FileTransfer,
166
+ 'mime-type' => filedesc[:mime]
167
+ }
168
+ file_args = {'name' => filedesc[:name],
169
+ 'size' => filedesc[:size],
170
+ 'hash' => filedesc[:md5],
171
+ 'date' => filedesc[:date]
172
+ }
173
+ iq = connection.iq_stanza('to'=>jid,'type'=>'set') do |xml|
174
+ xml.si({:xmlns => EM::Xmpp::Namespaces::StreamInitiation, :id => sid}.merge(si_args)) do |si|
175
+ si.file({:xmlns => EM::Xmpp::Namespaces::FileTransfer}.merge(file_args)) do |file|
176
+ file.desc filedesc[:description]
177
+ end
178
+ si.feature(:xmlns => EM::Xmpp::Namespaces::FeatureNeg) do |feat|
179
+ connection.build_submit_form(feat,form)
180
+ end
181
+ end
182
+ end
183
+ send_iq_stanza_fibered iq
184
+ end
185
+
186
+ def negotiation_reply(reply_id,form)
187
+ iq = connection.iq_stanza('to'=>jid,'type'=>'result','id'=>reply_id) do |xml|
188
+ xml.si(:xmlns => EM::Xmpp::Namespaces::StreamInitiation) do |si|
189
+ si.feature(:xmlns => EM::Xmpp::Namespaces::FeatureNeg) do |feat|
190
+ connection.build_submit_form(feat,form)
191
+ end
192
+ end
193
+ end
194
+ connection.send_stanza iq
195
+ end
196
+ end
197
+
198
+ class Avatar < Entity
199
+ Item = Struct.new(:sha1, :data, :width, :height, :mime) do
200
+ def id
201
+ sha1 || Digest::SHA1.hexdigest(data)
202
+ end
203
+ def b64
204
+ Base64.strict_encode64 data
205
+ end
206
+ def bytes
207
+ data.size
208
+ end
209
+ end
210
+
211
+ def publish(item)
212
+ publish_data item
213
+ publish_metadata item
214
+ end
215
+
216
+ def publish_data(item)
217
+ iq = connection.iq_stanza('type' => 'set','to' => jid) do |xml|
218
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |pubsub|
219
+ pubsub.publish(:node => EM::Xmpp::Namespaces::AvatarData) do |pub|
220
+ pub.item(:id => item.id) do |i|
221
+ i.data({:xmnls => EM::Xmpp::Namespaces::AvatarData}, item.b64)
222
+ end
223
+ end
224
+ end
225
+ end
226
+ send_iq_stanza_fibered iq
227
+ end
228
+
229
+ def publish_metadata(item)
230
+ iq = connection.iq_stanza('type' => 'set','to' => jid) do |xml|
231
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |pubsub|
232
+ pubsub.publish(:node => EM::Xmpp::Namespaces::AvatarMetaData) do |pub|
233
+ pub.item(:id => item.id) do |i|
234
+ i.metadata({:xmnls => EM::Xmpp::Namespaces::AvatarMetaData}) do |md|
235
+ md.info(:width => item.width, :height => item.height, :bytes => item.bytes, :type => item.mime, :id => item.id)
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+ send_iq_stanza_fibered iq
242
+ end
243
+
244
+ def remove
245
+ iq = connection.iq_stanza('type' => 'set','to' => jid) do |xml|
246
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |pubsub|
247
+ pubsub.publish(:node => EM::Xmpp::Namespaces::AvatarMetaData) do |pub|
248
+ pub.item(:id => "current") do |i|
249
+ i.metadata(:xmlns => EM::Xmpp::Namespaces::AvatarMetaData)
250
+ end
251
+ end
252
+ end
253
+ end
254
+ send_iq_stanza_fibered iq
255
+ end
256
+ end
257
+
258
+ class PubSub < Entity
259
+ # returns the pubsub entity for a specific node_id of this entity
260
+ def node(node_id)
261
+ pubsub(node_id)
262
+ end
263
+
264
+ # returns the node_id of this pubsub entity
265
+ def node_id
266
+ jid.resource
267
+ end
268
+
269
+ # requests the list of subscriptions on this PubSub service
270
+ # returns the iq context for the answer
271
+ def service_subscriptions
272
+ iq = connection.iq_stanza('to'=>jid.bare) do |xml|
273
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |pubsub|
274
+ pubsub.subscriptions
275
+ end
276
+ end
277
+ send_iq_stanza_fibered iq
278
+ end
279
+
280
+ # requests the list of affiliations for this PubSub service
281
+ # returns the iq context for the answer
282
+ def service_affiliations
283
+ iq = connection.iq_stanza('to'=>jid.bare) do |xml|
284
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |pubsub|
285
+ pubsub.affiliations
286
+ end
287
+ end
288
+ send_iq_stanza_fibered iq
289
+ end
290
+
291
+ # list the subscription on that node
292
+ # returns the iq context for the answer
293
+ def subscription_options(subscription_id=nil)
294
+ params = {}
295
+ params['subid'] = subscription_id if subscription_id
296
+ subscribee = connection.jid.bare
297
+
298
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'get') do |xml|
299
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |sub|
300
+ sub.options({'node' => node_id, 'jid'=>subscribee}.merge(params))
301
+ end
302
+ end
303
+
304
+ send_iq_stanza_fibered iq
305
+ end
306
+
307
+ # sets configuration options on this entity
308
+ # uses a DataForms form
309
+ # returns the iq context for the answer
310
+ def configure_subscription(form)
311
+ subscribee = connection.jid.bare
312
+
313
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
314
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |sub|
315
+ sub.options({'node' => node_id, 'jid'=>subscribee}) do |options|
316
+ connection.build_submit_form(options,form)
317
+ end
318
+ end
319
+ end
320
+
321
+ send_iq_stanza_fibered iq
322
+ end
323
+
324
+ # retrieve default configuration of this entity
325
+ # returns the iq context for the answer
326
+ def default_subscription_configuration
327
+ subscribee = connection.jid.bare
328
+ args = {'node' => node_id} if node_id
329
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'get') do |xml|
330
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |sub|
331
+ sub.default(args)
332
+ end
333
+ end
334
+
335
+ send_iq_stanza_fibered iq
336
+ end
337
+
338
+
339
+ # subscribe to this entity
340
+ # returns the iq context for the answer
341
+ def subscribe
342
+ subscribee = connection.jid.bare
343
+
344
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
345
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |sub|
346
+ sub.subscribe('node' => node_id, 'jid'=>subscribee)
347
+ end
348
+ end
349
+
350
+ send_iq_stanza_fibered iq
351
+ end
352
+
353
+ # subscribe and configure this entity at the same time
354
+ # see subscribe and configure
355
+ # returns the iq context for the answer
356
+ def subscribe_and_configure(form)
357
+ subscribee = connection.jid.bare
358
+
359
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
360
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |sub|
361
+ sub.subscribe('node' => node_id, 'jid'=>subscribee)
362
+ sub.options do |options|
363
+ connection.build_submit_form(options,form)
364
+ end
365
+ end
366
+ end
367
+
368
+ send_iq_stanza_fibered iq
369
+ end
370
+
371
+ # unsubscribes from this entity.
372
+ # One must provide a subscription-id if there
373
+ # are multiple subscriptions to this node.
374
+ # returns the iq context for the answer
375
+ def unsubscribe(subscription_id=nil)
376
+ params = {}
377
+ params['subid'] = subscription_id if subscription_id
378
+ subscribee = connection.jid.bare
379
+
380
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
381
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |sub|
382
+ sub.unsubscribe({'node' => node_id, 'jid'=>subscribee}.merge(params))
383
+ end
384
+ end
385
+
386
+ send_iq_stanza_fibered iq
387
+ end
388
+
389
+ # list items of this pubsub node
390
+ # max_items is the maximum number of answers to return in the answer
391
+ # item_ids is the list of IDs to select from the pubsub node
392
+ #
393
+ # returns the iq context for the answer
394
+ def items(max_items=nil,item_ids=nil)
395
+ params = {}
396
+ params['max_items'] = max_items if max_items
397
+ iq = connection.iq_stanza('to'=>jid.bare) do |xml|
398
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |retrieve|
399
+ retrieve.items({'node' => node_id}.merge(params)) do |items|
400
+ if item_ids.respond_to?(:each)
401
+ item_ids.each do |item_id|
402
+ items('id' => item_id)
403
+ end
404
+ end
405
+ end
406
+ end
407
+ end
408
+
409
+ send_iq_stanza_fibered iq
410
+ end
411
+
412
+ # publishes a payload to the pubsub node
413
+ # if the item_payload responds to :call (e.g., a proc)
414
+ # then the item_payload will receive :call method with, as unique parameter,
415
+ # the xml node of the xml builder. this method call should append an entry
416
+ # node with the payload
417
+ # otherwise it is just serialized in an entry node
418
+ #
419
+ # item_id is an optional ID to identify the payload, otherwise, the
420
+ # pubsub service will attribute an ID
421
+ #
422
+ # returns the iq context for the answer
423
+ def publish(item_payload,item_id=nil)
424
+ params = {}
425
+ params['id'] = item_id if item_id
426
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
427
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |pubsub|
428
+ pubsub.publish(:node => node_id) do |publish|
429
+ if item_payload.respond_to?(:call)
430
+ publish.item(params) { |payload| item_payload.call payload }
431
+ else
432
+ publish.item(params) do |item|
433
+ item.entry(item_payload)
434
+ end
435
+ end
436
+
437
+ end
438
+ end
439
+ end
440
+
441
+ send_iq_stanza_fibered iq
442
+ end
443
+
444
+ # Retracts an item on a PubSub node given it's item_id.
445
+ #
446
+ # returns the iq context for the answer
447
+ def retract(item_id=nil)
448
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
449
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |pubsub|
450
+ pubsub.retract(:node => node_id) do |retract|
451
+ retract.item(:id => item_id)
452
+ end
453
+ end
454
+ end
455
+
456
+ send_iq_stanza_fibered iq
457
+ end
458
+
459
+ # Creates the PubSub node.
460
+ #
461
+ # returns the iq context for the answer
462
+ def create
463
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
464
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |pubsub|
465
+ pubsub.create(:node => node_id)
466
+ end
467
+ end
468
+
469
+ send_iq_stanza_fibered iq
470
+ end
471
+
472
+
473
+ # Purges the PubSub node.
474
+ #
475
+ # returns the iq context for the answer
476
+ def purge
477
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
478
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSubOwner) do |pubsub|
479
+ pubsub.purge(:node => node_id)
480
+ end
481
+ end
482
+
483
+ send_iq_stanza_fibered iq
484
+ end
485
+
486
+ # requests the list of subscriptions on this pubsub node (for the owner)
487
+ # returns the iq context for the answer
488
+ def subscriptions
489
+ iq = connection.iq_stanza('to'=>jid.bare) do |xml|
490
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSubOwner) do |pubsub|
491
+ pubsub.subscriptions(:node => node_id)
492
+ end
493
+ end
494
+ send_iq_stanza_fibered iq
495
+ end
496
+
497
+ # requests the list of affiliations on this pubsub node (for the owner)
498
+ # returns the iq context for the answer
499
+ def affiliations
500
+ iq = connection.iq_stanza('to'=>jid.bare) do |xml|
501
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSubOwner) do |pubsub|
502
+ pubsub.affiliations(:node => node_id)
503
+ end
504
+ end
505
+ send_iq_stanza_fibered iq
506
+ end
507
+
508
+ # changes the subscription status of a pubsub node (for the owner)
509
+ # returns the iq context for the answer
510
+ def modify_subscriptions(subs)
511
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
512
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSubOwner) do |pubsub|
513
+ pubsub.subscriptions(:node => node_id) do |node|
514
+ subs.each do |s|
515
+ node.subscription(:jid => s.jid, :subscription => s.subscription, :subid => s.sub_id)
516
+ end
517
+ end
518
+ end
519
+ end
520
+ send_iq_stanza_fibered iq
521
+ end
522
+
523
+ # changes the affiliation status of a pubsub node (for the owner)
524
+ # returns the iq context for the answer
525
+ def modify_affiliations(affs)
526
+ affs = [affs].flatten
527
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
528
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSubOwner) do |pubsub|
529
+ pubsub.affiliations(:node => node_id) do |node|
530
+ affs.each do |s|
531
+ node.affiliation(:jid => s.jid, :affiliation => s.affiliation)
532
+ end
533
+ end
534
+ end
535
+ end
536
+ send_iq_stanza_fibered iq
537
+ end
538
+
539
+ # deletes the subscription of one or multiple subscribees of a pubsub node (for the owner)
540
+ # returns the iq context for the answer
541
+ def delete_subscriptions(jids,subids=nil)
542
+ jids = [jids].flatten
543
+ subids = [subids].flatten
544
+ subs = jids.zip(subids).map{|jid,subid| EM::Xmpp::Context::Contexts::PubsubMain::Subscription.new(jid, nil, 'none', subid, nil)}
545
+ modify_subscriptions subs
546
+ end
547
+
548
+ # deletes the affiliation of one or multiple subscribees of a pubsub node (for the owner)
549
+ # returns the iq context for the answer
550
+ def delete_affiliations(jids)
551
+ jids = [jids].flatten
552
+ affs = jids.map{|jid| EM::Xmpp::Context::Contexts::PubsubMain::Affiliation.new(jid, node_id, 'none')}
553
+ modify_affiliations affs
554
+ end
555
+
556
+ # Deletes the PubSub node.
557
+ # Optionnaly redirects the node.
558
+ #
559
+ # returns the iq context for the answer
560
+ def delete(redirect_uri=nil)
561
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
562
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSubOwner) do |pubsub|
563
+ pubsub.delete(:node => node_id) do |del|
564
+ del.redirect(:uri => redirect_uri) if redirect_uri
565
+ end
566
+ end
567
+ end
568
+
569
+ send_iq_stanza_fibered iq
570
+ end
571
+
572
+ # Creates the PubSub node with a non-default configuration.
573
+ #
574
+ # returns the iq context for the answer
575
+ def create_and_configure(form)
576
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
577
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSub) do |node|
578
+ node.create('node' => node_id)
579
+ node.configure do |options|
580
+ connection.build_submit_form(options,form)
581
+ end
582
+ end
583
+ end
584
+
585
+ send_iq_stanza_fibered iq
586
+ end
587
+
588
+
589
+ # requests the node configuration (for owners)
590
+ #
591
+ # returns the iq context for the answer
592
+ def configuration_options
593
+ iq = connection.iq_stanza('to'=>jid.bare) do |xml|
594
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSubOwner) do |node|
595
+ node.configure('node' => node_id)
596
+ end
597
+ end
598
+
599
+ send_iq_stanza_fibered iq
600
+ end
601
+
602
+ # configures the node (for owners)
603
+ #
604
+ # returns the iq context for the answer
605
+ def configure(form)
606
+ iq = connection.iq_stanza('to'=>jid.bare,'type'=>'set') do |xml|
607
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSubOwner) do |node|
608
+ node.configure('node' => node_id) do |config|
609
+ connection.build_submit_form(config,form)
610
+ end
611
+ end
612
+ end
613
+
614
+ send_iq_stanza_fibered iq
615
+ end
616
+
617
+ # retrieve default configuration of this pubsub service
618
+ #
619
+ # returns the iq context for the answer
620
+ def default_configuration
621
+ iq = connection.iq_stanza('to'=>jid.bare) do |xml|
622
+ xml.pubsub(:xmlns => EM::Xmpp::Namespaces::PubSubOwner) do |sub|
623
+ sub.default
624
+ end
625
+ end
626
+
627
+ send_iq_stanza_fibered iq
628
+ end
629
+
630
+
631
+ end
632
+
633
+ # Generates a MUC entity from this entity.
634
+ # If the nick argument is null then the entity is the MUC itself.
635
+ # If the nick argument is present, then the entity is the user with
636
+ # the corresponding nickname.
637
+ def muc(nick=nil)
638
+ muc_jid = JID.new jid.node, jid.domain, nick
639
+ Muc.new(connection, muc_jid)
109
640
  end
641
+
642
+ class Muc < Entity
643
+ # The room corresponding to this entity.
644
+ def room
645
+ muc(nil)
646
+ end
647
+
648
+ # Join a MUC.
649
+ def join(nick,pass=nil,historysize=0,&blk)
650
+ pres = connection.presence_stanza('to'=> muc(nick).to_s) do |xml|
651
+ xml.password pass if pass
652
+ xml.x('xmlns' => Namespaces::Muc) do |x|
653
+ x.history('maxchars' => historysize.to_s)
654
+ end
655
+ end
656
+ connection.send_stanza pres, &blk
657
+ end
658
+
659
+ # Leave a MUC.
660
+ def part(nick,msg=nil)
661
+ pres = connection.presence_stanza('to'=> muc(nick).to_s,'type'=>'unavailable') do |xml|
662
+ xml.status msg if msg
663
+ end
664
+ connection.send_stanza pres
665
+ end
666
+
667
+ # Changes nickname in this room.
668
+ def change_nick(nick)
669
+ join(nick)
670
+ end
671
+
672
+ # Say some message in the muc.
673
+ def say(body, xmlproc=nil, &blk)
674
+ msg = connection.message_stanza(:to => jid, :type => 'groupchat') do |xml|
675
+ xml.body body
676
+ xmlproc.call xml if xmlproc
677
+ end
678
+ connection.send_stanza msg, &blk
679
+ end
680
+
681
+ private
682
+
683
+ def set_role(role,nick,reason=nil,&blk)
684
+ iq = connection.iq_stanza(:to => jid,:type => 'set') do |xml|
685
+ xml.query('xmlns' => Namespaces::MucAdmin) do |q|
686
+ q.item('nick' => nick, 'role' => role) do |r|
687
+ r.reason reason if reason
688
+ end
689
+ end
690
+ end
691
+ send_iq_stanza_fibered iq
692
+ end
693
+
694
+ def set_affiliation(affiliation,affiliated_jid,reason=nil,&blk)
695
+ iq = connection.iq_stanza(:to => jid,:type => 'set') do |xml|
696
+ xml.query('xmlns' => Namespaces::MucAdmin) do |q|
697
+ q.item('affiliation' => affiliation, 'jid' => affiliated_jid) do |r|
698
+ r.reason reason if reason
699
+ end
700
+ end
701
+ end
702
+ send_iq_stanza_fibered iq
703
+ end
704
+
705
+ public
706
+
707
+ # kick a user (based on his nickname) from the channel
708
+ def kick(nick,reason="no reason",&blk)
709
+ set_role 'none', nick, reason, &blk
710
+ end
711
+
712
+ # voice a user (based on his nickname) in a channel
713
+ def voice(nick,reason=nil,&blk)
714
+ set_role 'participant', nick, reason, &blk
715
+ end
716
+
717
+ # remove voice flag from a user (based on his nickname) in a channel
718
+ def unvoice(nick,reason=nil,&blk)
719
+ set_role 'visitor', nick, reason, &blk
720
+ end
721
+
722
+ # set a ban on a user (from his bare JID) from the channel
723
+ def ban(jid,reason="no reason",&blk)
724
+ set_affiliation 'outcast', jid, reason, &blk
725
+ end
726
+
727
+ # lifts the ban on a user (from his bare JID) from the channel
728
+ def unban(jid,reason=nil,&blk)
729
+ set_affiliation 'none', jid, reason, &blk
730
+ end
731
+
732
+ # sets membership to the room
733
+ def member(jid,reason=nil,&blk)
734
+ set_affiliation 'member', jid, reason, &blk
735
+ end
736
+
737
+ # removes membership to the room
738
+ def unmember(jid,reason=nil,&blk)
739
+ set_affiliation 'none', jid, reason, &blk
740
+ end
741
+
742
+ # sets moderator status
743
+ def moderator(jid,reason=nil,&blk)
744
+ set_role 'moderator', jid, reason, &blk
745
+ end
746
+
747
+ # removes moderator status
748
+ def unmoderator(jid,reason=nil,&blk)
749
+ set_role 'participant', jid, reason, &blk
750
+ end
751
+
752
+ # gives ownership of the room
753
+ def owner(jid,reason=nil,&blk)
754
+ set_affiliation 'owner', jid, reason, &blk
755
+ end
756
+
757
+ # removes membership of the room
758
+ def unowner(jid,reason=nil,&blk)
759
+ set_affiliation 'admin', jid, reason, &blk
760
+ end
761
+
762
+ # gives admin rights on the room
763
+ def admin(jid,reason=nil,&blk)
764
+ set_affiliation 'admin', jid, reason, &blk
765
+ end
766
+
767
+ # removes admin rights on of the room
768
+ def unadmin(jid,reason=nil,&blk)
769
+ set_affiliation 'member', jid, reason, &blk
770
+ end
771
+
772
+ #TODO:
773
+ # get configure-form
774
+ # configure max users
775
+ # configure as reserved
776
+ # configure public jids
777
+ # create/destroy a room
778
+
779
+ # asks for a nickname registration
780
+ def register_nickname(nick)
781
+ #TODO: fiber blocks on getting the registration form
782
+ # user fills-in the form and submit
783
+ # rooms returns result
784
+ raise NotImplementedError
785
+ end
786
+
787
+ # request voice to the moderators
788
+ def request_voice(nick)
789
+ #TODO: fiber blocks on getting the registration form
790
+ # user fills-in the form and submit
791
+ # rooms returns result
792
+ raise NotImplementedError
793
+ end
794
+
795
+ # gets the list of registered nicknames for that list
796
+ def registered_nicknames
797
+ raise NotImplementedError
798
+ end
799
+
800
+ def voice_list
801
+ raise NotImplementedError
802
+ end
803
+
804
+ def banned_list
805
+ raise NotImplementedError
806
+ end
807
+
808
+ def owner_list
809
+ raise NotImplementedError
810
+ end
811
+
812
+ def admin_list
813
+ raise NotImplementedError
814
+ end
815
+
816
+ # sets the room subject (Message Of The Day)
817
+ def motd(subject,&blk)
818
+ msg = connection.message_stanza(:to => jid) do |xml|
819
+ xml.subject subject
820
+ end
821
+ connection.send_stanza msg, &blk
822
+ end
823
+
824
+ # invites someone (based on his jid) to the MUC
825
+ def invite(invited_jid,reason="no reason",&blk)
826
+ msg = connection.message_stanza(:to => jid) do |xml|
827
+ xml.x('xmlns' => Namespaces::MucUser) do |x|
828
+ x.invite('to' => invited_jid.to_s) do |invite|
829
+ invite.reason reason
830
+ end
831
+ end
832
+ end
833
+ connection.send_stanza msg, &blk
834
+ end
835
+
836
+ end
837
+
110
838
  end
111
839
  end