gloox  1.0.28
pubsubmanager.cpp
1 /*
2  Copyright (c) 2007-2023 by Jakob Schröter <js@camaya.net>
3  This file is part of the gloox library. http://camaya.net/gloox
4 
5  This software is distributed under a license. The full license
6  agreement can be found in the file LICENSE in this distribution.
7  This software may not be copied, modified, sold or distributed
8  other than expressed in the named license agreement.
9 
10  This software is distributed without any warranty.
11 */
12 
13 #include "pubsubmanager.h"
14 #include "clientbase.h"
15 #include "dataform.h"
16 #include "iq.h"
17 #include "pubsub.h"
18 #include "pubsubresulthandler.h"
19 #include "pubsubitem.h"
20 #include "shim.h"
21 #include "util.h"
22 #include "error.h"
23 
24 namespace gloox
25 {
26 
27  namespace PubSub
28  {
29 
30  static const std::string
31  XMLNS_PUBSUB_NODE_CONFIG = "http://jabber.org/protocol/pubsub#node_config",
32  XMLNS_PUBSUB_SUBSCRIBE_OPTIONS = "http://jabber.org/protocol/pubsub#subscribe_options";
33 
40 /* static PubSubFeature featureType( const std::string& str )
41  {
42  static const char* values [] = {
43  "collections",
44  "config-node",
45  "create-and-configure",
46  "create-nodes",
47  "delete-any",
48  "delete-nodes",
49  "get-pending",
50  "instant-nodes",
51  "item-ids",
52  "leased-subscription",
53  "manage-subscriptions",
54  "meta-data",
55  "modify-affiliations",
56  "multi-collection",
57  "multi-subscribe",
58  "outcast-affiliation",
59  "persistent-items",
60  "presence-notifications",
61  "publish",
62  "publisher-affiliation",
63  "purge-nodes",
64  "retract-items",
65  "retrieve-affiliations",
66  "retrieve-default",
67  "retrieve-items",
68  "retrieve-subscriptions",
69  "subscribe",
70  "subscription-options",
71  "subscription-notifications",
72  "owner",
73  "event",
74  };
75  return static_cast< PubSubFeature >( util::lookup2( str, values ) );
76  }
77 */
78 
79  static const char* subscriptionValues[] = {
80  "none", "subscribed", "pending", "unconfigured"
81  };
82 
83  static inline SubscriptionType subscriptionType( const std::string& subscription )
84  {
85  return static_cast<SubscriptionType>( util::lookup( subscription, subscriptionValues ) );
86  }
87 
88  static inline const std::string subscriptionValue( SubscriptionType subscription )
89  {
90  return util::lookup( subscription, subscriptionValues );
91  }
92 
93  static const char* affiliationValues[] = {
94  "none", "publisher", "owner", "outcast"
95  };
96 
97  static inline AffiliationType affiliationType( const std::string& affiliation )
98  {
99  return static_cast<AffiliationType>( util::lookup( affiliation, affiliationValues ) );
100  }
101 
102  static inline const std::string affiliationValue( AffiliationType affiliation )
103  {
104  return util::lookup( affiliation, affiliationValues );
105  }
106 
107  // ---- Manager::PubSubOwner ----
108  Manager::PubSubOwner::PubSubOwner( TrackContext context )
109  : StanzaExtension( ExtPubSubOwner ), m_ctx( context ), m_form( 0 )
110  {
111  }
112 
113  Manager::PubSubOwner::PubSubOwner( const Tag* tag )
114  : StanzaExtension( ExtPubSubOwner ), m_ctx( InvalidContext ), m_form( 0 )
115  {
116  const Tag* d = tag->findTag( "pubsub/delete" );
117  if( d )
118  {
119  m_ctx = DeleteNode;
120  m_node = d->findAttribute( "node" );
121  return;
122  }
123  const Tag* p = tag->findTag( "pubsub/purge" );
124  if( p )
125  {
126  m_ctx = PurgeNodeItems;
127  m_node = p->findAttribute( "node" );
128  return;
129  }
130  const Tag* c = tag->findTag( "pubsub/configure" );
131  if( c )
132  {
133  m_ctx = SetNodeConfig;
134  m_node = c->findAttribute( "node" );
135  if( c->hasChild( "x", "xmlns", XMLNS_X_DATA ) )
136  {
137  m_ctx = GetNodeConfig;
138  m_form = new DataForm( c->findChild( "x", "xmlns", XMLNS_X_DATA ) );
139  }
140  return;
141  }
142  const Tag* de = tag->findTag( "pubsub/default" );
143  if( de )
144  {
145  m_ctx = DefaultNodeConfig;
146  return;
147  }
148  const Tag* s = tag->findTag( "pubsub/subscriptions" );
149  if( s )
150  {
151  m_ctx = GetSubscriberList;
152  m_node = s->findAttribute( "node" );
153  const TagList& l = s->children();
154  TagList::const_iterator it =l.begin();
155  for( ; it != l.end(); ++it )
156  {
157  if( (*it)->name() == "subscription" )
158  {
159  Subscriber sub( (*it)->findAttribute( "jid" ),
160  subscriptionType( (*it)->findAttribute( "subscription" ) ),
161  (*it)->findAttribute( "subid" ) );
162  m_subList.push_back( sub );
163  }
164  }
165  return;
166  }
167  const Tag* a = tag->findTag( "pubsub/affiliations" );
168  if( a )
169  {
170  m_ctx = GetAffiliateList;
171  m_node = a->findAttribute( "node" );
172  const TagList& l = a->children();
173  TagList::const_iterator it = l.begin();
174  for( ; it != l.end(); ++it )
175  {
176  if( (*it)->name() == "affiliation" )
177  {
178  Affiliate aff( (*it)->findAttribute( "jid" ),
179  affiliationType( (*it)->findAttribute( "affiliation" ) ) );
180  m_affList.push_back( aff );
181  }
182  }
183  return;
184  }
185  }
186 
187  Manager::PubSubOwner::~PubSubOwner()
188  {
189  delete m_form;
190  }
191 
192  const std::string& Manager::PubSubOwner::filterString() const
193  {
194  static const std::string filter = "/iq/pubsub[@xmlns='" + XMLNS_PUBSUB_OWNER + "']";
195  return filter;
196  }
197 
198  Tag* Manager::PubSubOwner::tag() const
199  {
200  if( m_ctx == InvalidContext )
201  return 0;
202 
203  Tag* t = new Tag( "pubsub" );
204  t->setXmlns( XMLNS_PUBSUB_OWNER );
205  Tag* c = 0;
206 
207  switch( m_ctx )
208  {
209  case DeleteNode:
210  {
211  c = new Tag( t, "delete", "node", m_node );
212  break;
213  }
214  case PurgeNodeItems:
215  {
216  c = new Tag( t, "purge", "node", m_node );
217  break;
218  }
219  case GetNodeConfig:
220  case SetNodeConfig:
221  {
222  c = new Tag( t, "configure" );
223  c->addAttribute( "node", m_node );
224  if( m_form )
225  c->addChild( m_form->tag() );
226  break;
227  }
228  case GetSubscriberList:
229  case SetSubscriberList:
230 
231  {
232  c = new Tag( t, "subscriptions" );
233  c->addAttribute( "node", m_node );
234  if( m_subList.size() )
235  {
236  Tag* s;
237  SubscriberList::const_iterator it = m_subList.begin();
238  for( ; it != m_subList.end(); ++it )
239  {
240  s = new Tag( c, "subscription" );
241  s->addAttribute( "jid", (*it).jid.full() );
242  s->addAttribute( "subscription", util::lookup( (*it).type, subscriptionValues ) );
243  if( !(*it).subid.empty() )
244  s->addAttribute( "subid", (*it).subid );
245  }
246  }
247  break;
248  }
249  case GetAffiliateList:
250  case SetAffiliateList:
251  {
252  c = new Tag( t, "affiliations" );
253  c->addAttribute( "node", m_node );
254  if( m_affList.size() )
255  {
256  Tag* a;
257  AffiliateList::const_iterator it = m_affList.begin();
258  for( ; it != m_affList.end(); ++it )
259  {
260  a = new Tag( c, "affiliation", "jid", (*it).jid.full() );
261  a->addAttribute( "affiliation", util::lookup( (*it).type, affiliationValues ) );
262  }
263  }
264  break;
265  }
266  case DefaultNodeConfig:
267  {
268  c = new Tag( t, "default" );
269  break;
270  }
271  default:
272  break;
273  }
274 
275  return t;
276  }
277  // ---- ~Manager::PubSubOwner ----
278 
279  // ---- Manager::PubSub ----
280  Manager::PubSub::PubSub( TrackContext context )
281  : StanzaExtension( ExtPubSub ), m_ctx( context ), m_maxItems( 0 ),
282  m_notify( false )
283  {
284  m_options.df = 0;
285  }
286 
287  Manager::PubSub::PubSub( const Tag* tag )
288  : StanzaExtension( ExtPubSub ), m_ctx( InvalidContext ),
289  m_maxItems( 0 ), m_notify( false )
290  {
291  m_options.df = 0;
292  if( !tag )
293  return;
294 
295  const Tag* sl = tag->findTag( "pubsub/subscriptions" );
296  if( sl )
297  {
298  TagList l = sl->children();
299  if( l.size() )
300  {
301  m_ctx = GetSubscriptionList;
302  TagList::const_iterator it = l.begin();
303  for( ; it != l.end(); ++it )
304  {
305  const std::string& node = (*it)->findAttribute( "node" );
306  const std::string& sub = (*it)->findAttribute( "subscription" );
307  const std::string& subid = (*it)->findAttribute( "subid" );
308  SubscriptionInfo si;
309  si.jid.setJID( (*it)->findAttribute( "jid" ) );
310  si.type = subscriptionType( sub );
311  si.subid = subid;
312 
313  SubscriptionMap::iterator iter = m_subscriptionMap.find( node );
314  if( iter == m_subscriptionMap.end() )
315  {
316  iter = m_subscriptionMap.insert( std::make_pair( node, SubscriptionList() ) ).first;
317  }
318 
319  SubscriptionList& lst = iter->second;
320  lst.push_back( si );
321  }
322  return;
323  }
324  }
325  ConstTagList l = tag->findTagList( "pubsub/affiliations/affiliation" );
326  if( l.size() )
327  {
328  m_ctx = GetAffiliationList;
329  ConstTagList::const_iterator it = l.begin();
330  for( ; it != l.end(); ++it )
331  {
332  const std::string& node = (*it)->findAttribute( "node" );
333  const std::string& aff = (*it)->findAttribute( "affiliation" );
334  m_affiliationMap[node] = affiliationType( aff );
335  }
336  return;
337  }
338  const Tag* s = tag->findTag( "pubsub/subscribe" );
339  if( s )
340  {
341  m_ctx = Subscription;
342  m_node = s->findAttribute( "node" );
343  m_jid = s->findAttribute( "jid" );
344  }
345  const Tag* u = tag->findTag( "pubsub/unsubscribe" );
346  if( u )
347  {
348  m_ctx = Unsubscription;
349  m_node = u->findAttribute( "node" );
350  m_jid = u->findAttribute( "jid" );
351  m_subid = u->findAttribute( "subid" );
352  }
353  const Tag* o = tag->findTag( "pubsub/options" );
354  if( o )
355  {
356  if( m_ctx == InvalidContext )
357  {
358  Tag* parent = tag->parent();
359  if( parent && parent->findAttribute("type") == "set" )
360  m_ctx = SetSubscriptionOptions;
361  else
362  m_ctx = GetSubscriptionOptions;
363  }
364  if( m_ctx == SetSubscriptionOptions || m_ctx == GetSubscriptionOptions )
365  {
366  // We set both m_node and m_options.node for
367  // get/set options, since m_options.node is not exposed externally
368  m_node = o->findAttribute( "node" );
369  m_jid.setJID( o->findAttribute( "jid" ) );
370  m_subid = o->findAttribute( "subid" );
371  }
372  m_options.node = o->findAttribute( "node" );
373  m_options.df = new DataForm( o->findChild( "x", "xmlns", XMLNS_X_DATA ) );
374  }
375  const Tag* su = tag->findTag( "pubsub/subscription" );
376  if( su )
377  {
378  SubscriptionInfo si;
379  si.jid.setJID( su->findAttribute( "jid" ) );
380  si.subid = su->findAttribute( "subid" );
381  si.type = subscriptionType( su->findAttribute( "subscription" ) );
382  SubscriptionList& lst = m_subscriptionMap[su->findAttribute( "node" )];
383  lst.push_back( si );
384  return;
385  }
386  const Tag* i = tag->findTag( "pubsub/items" );
387  if( i )
388  {
389  m_ctx = RequestItems;
390  m_node = i->findAttribute( "node" );
391  m_subid = i->findAttribute( "subid" );
392  m_maxItems = atoi( i->findAttribute( "max_items" ).c_str() );
393  const TagList& l = i->children();
394  TagList::const_iterator it = l.begin();
395  for( ; it != l.end(); ++it )
396  m_items.push_back( new Item( (*it) ) );
397  return;
398  }
399  const Tag* p = tag->findTag( "pubsub/publish" );
400  if( p )
401  {
402  m_ctx = PublishItem;
403  m_node = p->findAttribute( "node" );
404  const TagList& l = p->children();
405  TagList::const_iterator it = l.begin();
406  for( ; it != l.end(); ++it )
407  m_items.push_back( new Item( (*it) ) );
408  return;
409  }
410  const Tag* r = tag->findTag( "pubsub/retract" );
411  if( r )
412  {
413  m_ctx = DeleteItem;
414  m_node = r->findAttribute( "node" );
415  m_notify = r->hasAttribute( "notify", "1" ) || r->hasAttribute( "notify", "true" );
416  const TagList& l = r->children();
417  TagList::const_iterator it = l.begin();
418  for( ; it != l.end(); ++it )
419  m_items.push_back( new Item( (*it) ) );
420  return;
421  }
422  const Tag* c = tag->findTag( "pubsub/create" );
423  if( c )
424  {
425  m_ctx = CreateNode;
426  m_node = c->findAttribute( "node" );
427  const Tag* config = tag->findTag( "pubsub/configure" );
428  if( config && config->hasChild( "x", XMLNS_X_DATA ) )
429  m_options.df = new DataForm( config->findChild( "x", XMLNS_X_DATA ) );
430  }
431  }
432 
433  Manager::PubSub::~PubSub()
434  {
435  delete m_options.df;
436  util::clearList( m_items );
437  }
438 
439  const std::string& Manager::PubSub::filterString() const
440  {
441  static const std::string filter = "/iq/pubsub[@xmlns='" + XMLNS_PUBSUB + "']";
442  return filter;
443  }
444 
445  Tag* Manager::PubSub::tag() const
446  {
447  if( m_ctx == InvalidContext )
448  return 0;
449 
450  Tag* t = new Tag( "pubsub" );
451  t->setXmlns( XMLNS_PUBSUB );
452 
453  if( m_ctx == GetSubscriptionList )
454  {
455  Tag* sub = new Tag( t, "subscriptions" );
456  SubscriptionMap::const_iterator it = m_subscriptionMap.begin();
457  for( ; it != m_subscriptionMap.end(); ++it )
458  {
459  const SubscriptionList& lst = (*it).second;
460  SubscriptionList::const_iterator it2 = lst.begin();
461  for( ; it2 != lst.end(); ++it2 )
462  {
463  Tag* s = new Tag( sub, "subscription" );
464  s->addAttribute( "node", (*it).first );
465  s->addAttribute( "jid", (*it2).jid );
466  s->addAttribute( "subscription", subscriptionValue( (*it2).type ) );
467  s->addAttribute( "sid", (*it2).subid );
468  }
469  }
470  }
471  else if( m_ctx == GetAffiliationList )
472  {
473 
474  Tag* aff = new Tag( t, "affiliations" );
475  AffiliationMap::const_iterator it = m_affiliationMap.begin();
476  for( ; it != m_affiliationMap.end(); ++it )
477  {
478  Tag* a = new Tag( aff, "affiliation" );
479  a->addAttribute( "node", (*it).first );
480  a->addAttribute( "affiliation", affiliationValue( (*it).second ) );
481  }
482  }
483  else if( m_ctx == Subscription )
484  {
485  Tag* s = new Tag( t, "subscribe" );
486  s->addAttribute( "node", m_node );
487  s->addAttribute( "jid", m_jid.full() );
488  if( m_options.df )
489  {
490  Tag* o = new Tag( t, "options" );
491  o->addChild( m_options.df->tag() );
492  }
493  }
494  else if( m_ctx == Unsubscription )
495  {
496  Tag* u = new Tag( t, "unsubscribe" );
497  u->addAttribute( "node", m_node );
498  u->addAttribute( "jid", m_jid.full() );
499  u->addAttribute( "subid", m_subid );
500  }
501  else if( m_ctx == GetSubscriptionOptions
502  || m_ctx == SetSubscriptionOptions )
503  {
504  Tag* o = new Tag( t, "options" );
505  o->addAttribute( "node", m_options.node );
506  o->addAttribute( "jid", m_jid.full() );
507  if( !m_subid.empty() )
508  o->addAttribute( "subid", m_subid );
509  if( m_options.df )
510  o->addChild( m_options.df->tag() );
511  }
512  else if( m_ctx == RequestItems )
513  {
514  Tag* i = new Tag( t, "items" );
515  i->addAttribute( "node", m_node );
516  if( m_maxItems )
517  i->addAttribute( "max_items", m_maxItems );
518  i->addAttribute( "subid", m_subid );
519  ItemList::const_iterator it = m_items.begin();
520  for( ; it != m_items.end(); ++it )
521  i->addChild( (*it)->tag() );
522  }
523  else if( m_ctx == PublishItem )
524  {
525  Tag* p = new Tag( t, "publish" );
526  p->addAttribute( "node", m_node );
527  ItemList::const_iterator it = m_items.begin();
528  for( ; it != m_items.end(); ++it )
529  p->addChild( (*it)->tag() );
530  if( m_options.df )
531  {
532  Tag* po = new Tag( p, "publish-options" );
533  po->addChild( m_options.df->tag() );
534  }
535  }
536  else if( m_ctx == DeleteItem )
537  {
538  Tag* r = new Tag( t, "retract" );
539  r->addAttribute( "node", m_node );
540  if( m_notify )
541  r->addAttribute( "notify", "true" );
542  ItemList::const_iterator it = m_items.begin();
543  for( ; it != m_items.end(); ++it )
544  r->addChild( (*it)->tag() );
545  }
546  else if( m_ctx == CreateNode )
547  {
548  Tag* c = new Tag( t, "create" );
549  if( !m_node.empty() )
550  c->addAttribute( "node", m_node );
551  Tag* config = new Tag( t, "configure" );
552  if( m_options.df )
553  config->addChild( m_options.df->tag() );
554  }
555  return t;
556  }
557 
558  StanzaExtension* Manager::PubSub::clone() const
559  {
560  PubSub* p = new PubSub();
561  p->m_affiliationMap = m_affiliationMap;
562  p->m_subscriptionMap = m_subscriptionMap;
563  p->m_ctx = m_ctx;
564 
565  p->m_options.node = m_options.node;
566  p->m_options.df = m_options.df ? new DataForm( *(m_options.df) ) : 0;
567 
568  p->m_jid = m_jid;
569  p->m_node = m_node;
570  p->m_subid = m_subid;
571  ItemList::const_iterator it = m_items.begin();
572  for( ; it != m_items.end(); ++it )
573  p->m_items.push_back( new Item( *(*it) ) );
574 
575  p->m_maxItems = m_maxItems;
576  p->m_notify = m_notify;
577  return p;
578  }
579  // ---- ~Manager::PubSub ----
580 
581  // ---- Manager ----
582  Manager::Manager( ClientBase* parent )
583  : m_parent( parent )
584  {
585  if( m_parent )
586  {
587  m_parent->registerStanzaExtension( new PubSub() );
588  m_parent->registerStanzaExtension( new PubSubOwner() );
589  m_parent->registerStanzaExtension( new SHIM() );
590  }
591  }
592 
593  const std::string Manager::getSubscriptionsOrAffiliations( const JID& service,
594  ResultHandler* handler,
595  TrackContext context )
596  {
597  if( !m_parent || !handler || !service || context == InvalidContext )
598  return EmptyString;
599 
600  const std::string& id = m_parent->getID();
601  IQ iq( IQ::Get, service, id );
602  iq.addExtension( new PubSub( context ) );
603 
604  m_trackMapMutex.lock();
605  m_resultHandlerTrackMap[id] = handler;
606  m_trackMapMutex.unlock();
607  m_parent->send( iq, this, context );
608  return id;
609  }
610 
611  const std::string Manager::subscribe( const JID& service,
612  const std::string& node,
613  ResultHandler* handler,
614  const JID& jid,
615  SubscriptionObject type,
616  int depth,
617  const std::string& expire
618  )
619  {
620  if( !m_parent || !handler || !service || node.empty() )
621  return EmptyString;
622 
623  DataForm* options = 0;
624  if( type != SubscriptionNodes || depth != 1 )
625  {
626  options = new DataForm( TypeSubmit );
627  options->addField( DataFormField::TypeHidden, "FORM_TYPE", XMLNS_PUBSUB_SUBSCRIBE_OPTIONS );
628 
629  if( type == SubscriptionItems )
630  options->addField( DataFormField::TypeNone, "pubsub#subscription_type", "items" );
631 
632  if( depth != 1 )
633  {
634  DataFormField* field = options->addField( DataFormField::TypeNone, "pubsub#subscription_depth" );
635  if( depth == 0 )
636  field->setValue( "all" );
637  else
638  field->setValue( util::int2string( depth ) );
639  }
640 
641  if( !expire.empty() )
642  {
643  DataFormField* field = options->addField( DataFormField::TypeNone, "pubsub#expire" );
644  field->setValue( expire );
645  }
646  }
647 
648  return subscribe( service, node, handler, jid, options );
649  }
650 
651  const std::string Manager::subscribe( const JID& service,
652  const std::string& node,
653  ResultHandler* handler,
654  const JID& jid,
655  DataForm* options
656  )
657  {
658  if( !m_parent || !handler || !service || node.empty() )
659  return EmptyString;
660 
661  const std::string& id = m_parent->getID();
662  IQ iq( IQ::Set, service, id );
663  PubSub* ps = new PubSub( Subscription );
664  ps->setJID( jid ? jid : m_parent->jid() );
665  ps->setNode( node );
666  if( options != NULL )
667  ps->setOptions( node, options );
668  iq.addExtension( ps );
669 
670  m_trackMapMutex.lock();
671  m_resultHandlerTrackMap[id] = handler;
672  m_nopTrackMap[id] = node;
673  m_trackMapMutex.unlock();
674  m_parent->send( iq, this, Subscription );
675  return id;
676  }
677 
678  const std::string Manager::unsubscribe( const JID& service,
679  const std::string& node,
680  const std::string& subid,
681  ResultHandler* handler,
682  const JID& jid )
683  {
684  if( !m_parent || !handler || !service )
685  return EmptyString;
686 
687  const std::string& id = m_parent->getID();
688  IQ iq( IQ::Set, service, id );
689  PubSub* ps = new PubSub( Unsubscription );
690  ps->setNode( node );
691  ps->setJID( jid ? jid : m_parent->jid() );
692  ps->setSubscriptionID( subid );
693  iq.addExtension( ps );
694 
695  m_trackMapMutex.lock();
696  m_resultHandlerTrackMap[id] = handler;
697  m_trackMapMutex.unlock();
698  // FIXME? need to track info for handler
699  m_parent->send( iq, this, Unsubscription );
700  return id;
701  }
702 
703  const std::string Manager::subscriptionOptions( TrackContext context,
704  const JID& service,
705  const JID& jid,
706  const std::string& node,
707  ResultHandler* handler,
708  DataForm* df,
709  const std::string& subid )
710  {
711  if( !m_parent || !handler || !service )
712  return EmptyString;
713 
714  const std::string& id = m_parent->getID();
715  IQ iq( df ? IQ::Set : IQ::Get, service, id );
716  PubSub* ps = new PubSub( context );
717  ps->setJID( jid ? jid : m_parent->jid() );
718  if( !subid.empty() )
719  ps->setSubscriptionID( subid );
720  ps->setOptions( node, df );
721  iq.addExtension( ps );
722 
723  m_trackMapMutex.lock();
724  m_resultHandlerTrackMap[id] = handler;
725  m_trackMapMutex.unlock();
726  m_parent->send( iq, this, context );
727  return id;
728  }
729 
730  const std::string Manager::requestItems( const JID& service,
731  const std::string& node,
732  const std::string& subid,
733  int maxItems,
734  ResultHandler* handler )
735  {
736  if( !m_parent || !service || !handler )
737  return EmptyString;
738 
739  const std::string& id = m_parent->getID();
740  IQ iq( IQ::Get, service, id );
741  PubSub* ps = new PubSub( RequestItems );
742  ps->setNode( node );
743  ps->setSubscriptionID( subid );
744  ps->setMaxItems( maxItems );
745  iq.addExtension( ps );
746 
747  m_trackMapMutex.lock();
748  m_resultHandlerTrackMap[id] = handler;
749  m_trackMapMutex.unlock();
750  m_parent->send( iq, this, RequestItems );
751  return id;
752  }
753 
754  const std::string Manager::requestItems( const JID& service,
755  const std::string& node,
756  const std::string& subid,
757  const ItemList& items,
758  ResultHandler* handler )
759  {
760  if( !m_parent || !service || !handler )
761  return EmptyString;
762 
763  const std::string& id = m_parent->getID();
764  IQ iq( IQ::Get, service, id );
765  PubSub* ps = new PubSub( RequestItems );
766  ps->setNode( node );
767  ps->setSubscriptionID( subid );
768  ps->setItems( items );
769  iq.addExtension( ps );
770 
771  m_trackMapMutex.lock();
772  m_resultHandlerTrackMap[id] = handler;
773  m_trackMapMutex.unlock();
774  m_parent->send( iq, this, RequestItems );
775  return id;
776  }
777 
778  const std::string Manager::publishItem( const JID& service,
779  const std::string& node,
780  ItemList& items,
781  DataForm* options,
782  ResultHandler* handler )
783  {
784  if( !m_parent || !handler )
785  {
786  util::clearList( items );
787  return EmptyString;
788  }
789 
790  const std::string& id = m_parent->getID();
791  IQ iq( IQ::Set, service, id );
792  PubSub* ps = new PubSub( PublishItem );
793  ps->setNode( node );
794  ps->setItems( items );
795  ps->setOptions( EmptyString, options );
796  iq.addExtension( ps );
797 
798  m_trackMapMutex.lock();
799  m_resultHandlerTrackMap[id] = handler;
800  m_trackMapMutex.unlock();
801  m_parent->send( iq, this, PublishItem );
802  return id;
803  }
804 
805  const std::string Manager::deleteItem( const JID& service,
806  const std::string& node,
807  const ItemList& items,
808  bool notify,
809  ResultHandler* handler )
810  {
811  if( !m_parent || !handler || !service )
812  return EmptyString;
813 
814  const std::string& id = m_parent->getID();
815  IQ iq( IQ::Set, service, id );
816  PubSub* ps = new PubSub( DeleteItem );
817  ps->setNode( node );
818  ps->setItems( items );
819  ps->setNotify( notify );
820  iq.addExtension( ps );
821 
822  m_trackMapMutex.lock();
823  m_resultHandlerTrackMap[id] = handler;
824  m_trackMapMutex.unlock();
825  m_parent->send( iq, this, DeleteItem );
826  return id;
827  }
828 
829  const std::string Manager::createNode( const JID& service,
830  const std::string& node,
831  DataForm* config,
832  ResultHandler* handler )
833  {
834  if( !m_parent || !handler || !service )
835  return EmptyString;
836 
837  const std::string& id = m_parent->getID();
838  IQ iq( IQ::Set, service, id );
839  PubSub* ps = new PubSub( CreateNode );
840  if( !node.empty() )
841  ps->setNode( node );
842  ps->setOptions( EmptyString, config );
843  iq.addExtension( ps );
844 
845  m_trackMapMutex.lock();
846  m_nopTrackMap[id] = node;
847  m_resultHandlerTrackMap[id] = handler;
848  m_trackMapMutex.unlock();
849  m_parent->send( iq, this, CreateNode );
850  return id;
851  }
852 
853  const std::string Manager::deleteNode( const JID& service,
854  const std::string& node,
855  ResultHandler* handler )
856  {
857  if( !m_parent || !handler || !service || node.empty() )
858  return EmptyString;
859 
860  const std::string& id = m_parent->getID();
861  IQ iq( IQ::Set, service, id );
862  PubSubOwner* pso = new PubSubOwner( DeleteNode );
863  pso->setNode( node );
864  iq.addExtension( pso );
865 
866  m_trackMapMutex.lock();
867  m_nopTrackMap[id] = node;
868  m_resultHandlerTrackMap[id] = handler;
869  m_trackMapMutex.unlock();
870  m_parent->send( iq, this, DeleteNode );
871  return id;
872  }
873 
874  const std::string Manager::getDefaultNodeConfig( const JID& service,
875  NodeType type,
876  ResultHandler* handler )
877  {
878  if( !m_parent || !handler || !service )
879  return EmptyString;
880 
881  const std::string& id = m_parent->getID();
882  IQ iq( IQ::Get, service, id );
883  PubSubOwner* pso = new PubSubOwner( DefaultNodeConfig );
884  if( type == NodeCollection )
885  {
886  DataForm* df = new DataForm( TypeSubmit );
887  df->addField( DataFormField::TypeHidden, "FORM_TYPE", XMLNS_PUBSUB_NODE_CONFIG );
888  df->addField( DataFormField::TypeNone, "pubsub#node_type", "collection" );
889  pso->setConfig( df );
890  }
891  iq.addExtension( pso );
892 
893  m_trackMapMutex.lock();
894  m_resultHandlerTrackMap[id] = handler;
895  m_trackMapMutex.unlock();
896  m_parent->send( iq, this, DefaultNodeConfig );
897  return id;
898  }
899 
900  const std::string Manager::nodeConfig( const JID& service,
901  const std::string& node,
902  DataForm* config,
903  ResultHandler* handler )
904  {
905  if( !m_parent || !handler || !service || node.empty() )
906  return EmptyString;
907 
908  const std::string& id = m_parent->getID();
909  IQ iq( config ? IQ::Set : IQ::Get, service, id );
910  PubSubOwner* pso = new PubSubOwner( config ? SetNodeConfig : GetNodeConfig );
911  pso->setNode( node );
912  if( config )
913  pso->setConfig( config );
914  iq.addExtension( pso );
915 
916  m_trackMapMutex.lock();
917  m_nopTrackMap[id] = node;
918  m_resultHandlerTrackMap[id] = handler;
919  m_trackMapMutex.unlock();
920  m_parent->send( iq, this, config ? SetNodeConfig : GetNodeConfig );
921  return id;
922  }
923 
924  const std::string Manager::subscriberList( TrackContext ctx,
925  const JID& service,
926  const std::string& node,
927  const SubscriberList& subList,
928  ResultHandler* handler )
929  {
930  if( !m_parent || !handler || !service || node.empty() )
931  return EmptyString;
932 
933  const std::string& id = m_parent->getID();
934  IQ iq( ctx == SetSubscriberList ? IQ::Set : IQ::Get, service, id );
935  PubSubOwner* pso = new PubSubOwner( ctx );
936  pso->setNode( node );
937  pso->setSubscriberList( subList );
938  iq.addExtension( pso );
939 
940  m_trackMapMutex.lock();
941  m_nopTrackMap[id] = node;
942  m_resultHandlerTrackMap[id] = handler;
943  m_trackMapMutex.unlock();
944  m_parent->send( iq, this, ctx );
945  return id;
946  }
947 
948  const std::string Manager::affiliateList( TrackContext ctx,
949  const JID& service,
950  const std::string& node,
951  const AffiliateList& affList,
952  ResultHandler* handler )
953  {
954  if( !m_parent || !handler || !service || node.empty() )
955  return EmptyString;
956 
957  const std::string& id = m_parent->getID();
958  IQ iq( ctx == SetAffiliateList ? IQ::Set : IQ::Get, service, id );
959  PubSubOwner* pso = new PubSubOwner( ctx );
960  pso->setNode( node );
961  pso->setAffiliateList( affList );
962  iq.addExtension( pso );
963 
964  m_trackMapMutex.lock();
965  m_nopTrackMap[id] = node;
966  m_resultHandlerTrackMap[id] = handler;
967  m_trackMapMutex.unlock();
968  m_parent->send( iq, this, ctx );
969  return id;
970  }
971 
972  const std::string Manager::purgeNode( const JID& service,
973  const std::string& node,
974  ResultHandler* handler )
975  {
976  if( !m_parent || !handler || !service || node.empty() )
977  return EmptyString;
978 
979  const std::string& id = m_parent->getID();
980  IQ iq( IQ::Set, service, id );
981  PubSubOwner* pso = new PubSubOwner( PurgeNodeItems );
982  pso->setNode( node );
983  iq.addExtension( pso );
984 
985  m_trackMapMutex.lock();
986  m_nopTrackMap[id] = node;
987  m_resultHandlerTrackMap[id] = handler;
988  m_trackMapMutex.unlock();
989  m_parent->send( iq, this, PurgeNodeItems );
990  return id;
991  }
992 
993  bool Manager::removeID( const std::string& id )
994  {
995  m_trackMapMutex.lock();
996  ResultHandlerTrackMap::iterator ith = m_resultHandlerTrackMap.find( id );
997  if( ith == m_resultHandlerTrackMap.end() )
998  {
999  m_trackMapMutex.unlock();
1000  return false;
1001  }
1002  m_resultHandlerTrackMap.erase( ith );
1003  m_trackMapMutex.unlock();
1004  return true;
1005  }
1006 
1007  void Manager::handleIqID( const IQ& iq, int context )
1008  {
1009  const JID& service = iq.from();
1010  const std::string& id = iq.id();
1011 
1012  m_trackMapMutex.lock();
1013  ResultHandlerTrackMap::iterator ith = m_resultHandlerTrackMap.find( id );
1014  if( ith == m_resultHandlerTrackMap.end() )
1015  {
1016  m_trackMapMutex.unlock();
1017  return;
1018  }
1019  ResultHandler* rh = (*ith).second;
1020  m_resultHandlerTrackMap.erase( ith );
1021  m_trackMapMutex.unlock();
1022 
1023  switch( iq.subtype() )
1024  {
1025  case IQ::Error:
1026  case IQ::Result:
1027  {
1028  const Error* error = iq.error();
1029  switch( context )
1030  {
1031  case Subscription:
1032  {
1033  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1034  if( !ps )
1035  return;
1036  SubscriptionMap sm = ps->subscriptions();
1037  if( sm.empty() )
1038  {
1039  /* If the reply is an error, it will not contain a
1040  "subscription" child. It may however contain the
1041  original "subscribe" request. If that is the case, then
1042  we can still retrieve the node and JID of the request and
1043  notify the handler callback of the error. */
1044  if( ps->context() == Subscription )
1045  rh->handleSubscriptionResult( id, service, ps->node(), "", ps->jid(),
1046  SubscriptionInvalid, error );
1047  }
1048  else
1049  {
1050  SubscriptionMap::const_iterator it = sm.begin();
1051  const SubscriptionList& lst = (*it).second;
1052  if( lst.size() == 1 )
1053  {
1054  SubscriptionList::const_iterator it2 = lst.begin();
1055  rh->handleSubscriptionResult( id, service, (*it).first, (*it2).subid, (*it2).jid,
1056  (*it2).type, error );
1057  }
1058  }
1059  break;
1060  }
1061  case Unsubscription:
1062  {
1063  rh->handleUnsubscriptionResult( iq.id(), service, error );
1064  break;
1065  }
1066  case GetSubscriptionList:
1067  {
1068  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1069  if( !ps )
1070  return;
1071 
1072  rh->handleSubscriptions( id, service,
1073  ps->subscriptions(),
1074  error );
1075  break;
1076  }
1077  case GetAffiliationList:
1078  {
1079  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1080  if( !ps )
1081  return;
1082 
1083  rh->handleAffiliations( id, service,
1084  ps->affiliations(),
1085  error );
1086  break;
1087  }
1088  case RequestItems:
1089  {
1090  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1091  if( !ps )
1092  return;
1093 
1094  rh->handleItems( id, service, ps->node(),
1095  ps->items(), error );
1096  break;
1097  }
1098  case PublishItem:
1099  {
1100  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1101  rh->handleItemPublication( id, service, ps->node(),
1102  ps ? ps->items() : ItemList(),
1103  error );
1104  break;
1105  }
1106  case DeleteItem:
1107  {
1108  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1109  if( ps )
1110  {
1111  rh->handleItemDeletion( id, service,
1112  ps->node(),
1113  ps->items(),
1114  error );
1115  }
1116  break;
1117  }
1118  case DefaultNodeConfig:
1119  {
1120  const PubSubOwner* pso = iq.findExtension<PubSubOwner>( ExtPubSubOwner );
1121  if( pso )
1122  {
1123  rh->handleDefaultNodeConfig( id, service,
1124  pso->config(),
1125  error );
1126  }
1127  break;
1128  }
1129  case GetSubscriptionOptions:
1130  case GetSubscriberList:
1131  case SetSubscriberList:
1132  case GetAffiliateList:
1133  case SetAffiliateList:
1134  case GetNodeConfig:
1135  case SetNodeConfig:
1136  case CreateNode:
1137  case DeleteNode:
1138  case PurgeNodeItems:
1139  {
1140  switch( context )
1141  {
1142  case GetSubscriptionOptions:
1143  {
1144  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1145  if( ps )
1146  {
1147  rh->handleSubscriptionOptions( id, service,
1148  ps->jid(),
1149  ps->node(),
1150  ps->options(),
1151  ps->subscriptionID(),
1152  error );
1153  }
1154  break;
1155  }
1156  case GetSubscriberList:
1157  {
1158  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1159  if( ps )
1160  {
1161  const SubscriptionMap& sm = ps->subscriptions();
1162  SubscriptionMap::const_iterator itsm = sm.find( ps->node() );
1163  if( itsm != sm.end() )
1164  rh->handleSubscribers( iq.id(), service, ps->node(), (*itsm).second, 0 );
1165  }
1166  break;
1167  }
1168  case SetSubscriptionOptions:
1169  case SetSubscriberList:
1170  case SetAffiliateList:
1171  case SetNodeConfig:
1172  case CreateNode:
1173  case DeleteNode:
1174  case PurgeNodeItems:
1175  {
1176  m_trackMapMutex.lock();
1177  NodeOperationTrackMap::iterator it = m_nopTrackMap.find( id );
1178  if( it != m_nopTrackMap.end() )
1179  {
1180  const std::string& node = (*it).second;
1181  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1182  switch( context )
1183  {
1184  case SetSubscriptionOptions:
1185  {
1186  if( ps )
1187  {
1188  rh->handleSubscriptionOptionsResult( id, service,
1189  ps->jid(),
1190  node,
1191  ps->subscriptionID(),
1192  error );
1193  }
1194  else
1195  {
1196  rh->handleSubscriptionOptionsResult( id, service, JID( /* FIXME */ ), node, /* FIXME */ EmptyString, error );
1197  }
1198  break;
1199  }
1200  case SetSubscriberList:
1201  rh->handleSubscribersResult( id, service, node, 0, error );
1202  break;
1203  case SetAffiliateList:
1204  rh->handleAffiliatesResult( id, service, node, 0, error );
1205  break;
1206  case SetNodeConfig:
1207  rh->handleNodeConfigResult( id, service, node, error );
1208  break;
1209  case CreateNode:
1210  if( ps )
1211  rh->handleNodeCreation( id, service, ps->node(), error );
1212  else
1213  rh->handleNodeCreation( id, service, node, error );
1214  break;
1215  case DeleteNode:
1216  rh->handleNodeDeletion( id, service, node, error );
1217  break;
1218  case PurgeNodeItems:
1219  rh->handleNodePurge( id, service, node, error );
1220  break;
1221  }
1222  m_nopTrackMap.erase( it );
1223  }
1224  m_trackMapMutex.unlock();
1225  break;
1226  }
1227  case GetAffiliateList:
1228  {
1229  const PubSubOwner* pso = iq.findExtension<PubSubOwner>( ExtPubSubOwner );
1230  if( pso )
1231  {
1232  rh->handleAffiliates( id, service, pso->node(), pso->affiliateList(), error );
1233  }
1234  break;
1235  }
1236  case GetNodeConfig:
1237  {
1238  const PubSubOwner* pso = iq.findExtension<PubSubOwner>( ExtPubSubOwner );
1239  if( pso )
1240  {
1241  rh->handleNodeConfig( id, service,
1242  pso->node(),
1243  pso->config(),
1244  error );
1245  }
1246  break;
1247  }
1248  default:
1249  break;
1250  }
1251 
1252  break;
1253  }
1254  }
1255  break;
1256  }
1257  default:
1258  break;
1259  }
1260 
1261  }
1262 
1263  }
1264 
1265 }
1266 
This is the common base class for a Jabber/XMPP Client and a Jabber Component.
Definition: clientbase.h:79
const std::string getID()
const JID & jid()
Definition: clientbase.h:147
void send(Tag *tag)
void registerStanzaExtension(StanzaExtension *ext)
virtual void addField(DataFormField *field)
An abstraction of a single field in a XEP-0004 Data Form.
Definition: dataformfield.h:34
void setValue(const std::string &value)
An abstraction of a XEP-0004 Data Form.
Definition: dataform.h:57
A stanza error abstraction implemented as a StanzaExtension.
Definition: error.h:35
An abstraction of an IQ stanza.
Definition: iq.h:34
IqType subtype() const
Definition: iq.h:74
@ Set
Definition: iq.h:46
@ Error
Definition: iq.h:49
@ Result
Definition: iq.h:48
@ Get
Definition: iq.h:45
An abstraction of a JID.
Definition: jid.h:31
const std::string subscribe(const JID &service, const std::string &node, ResultHandler *handler, const JID &jid=JID(), SubscriptionObject type=SubscriptionNodes, int depth=1, const std::string &expire=EmptyString)
const std::string createNode(const JID &service, const std::string &node, DataForm *config, ResultHandler *handler)
bool removeID(const std::string &id)
const std::string requestItems(const JID &service, const std::string &node, const std::string &subid, int maxItems, ResultHandler *handler)
const std::string unsubscribe(const JID &service, const std::string &node, const std::string &subid, ResultHandler *handler, const JID &jid=JID())
virtual void handleIqID(const IQ &iq, int context)
const std::string deleteItem(const JID &service, const std::string &node, const ItemList &items, bool notify, ResultHandler *handler)
const std::string getDefaultNodeConfig(const JID &service, NodeType type, ResultHandler *handler)
const std::string purgeNode(const JID &service, const std::string &node, ResultHandler *handler)
const std::string publishItem(const JID &service, const std::string &node, ItemList &items, DataForm *options, ResultHandler *handler)
const std::string deleteNode(const JID &service, const std::string &node, ResultHandler *handler)
A virtual interface to receive item related requests results.
virtual void handleItems(const std::string &id, const JID &service, const std::string &node, const ItemList &itemList, const Error *error=0)=0
virtual void handleSubscriptionOptionsResult(const std::string &id, const JID &service, const JID &jid, const std::string &node, const std::string &sid=EmptyString, const Error *error=0)=0
virtual void handleAffiliations(const std::string &id, const JID &service, const AffiliationMap &affMap, const Error *error=0)=0
virtual void handleDefaultNodeConfig(const std::string &id, const JID &service, const DataForm *config, const Error *error=0)=0
virtual void handleNodeDeletion(const std::string &id, const JID &service, const std::string &node, const Error *error=0)=0
virtual void handleNodeConfigResult(const std::string &id, const JID &service, const std::string &node, const Error *error=0)=0
virtual void handleSubscriptionOptions(const std::string &id, const JID &service, const JID &jid, const std::string &node, const DataForm *options, const std::string &sid=EmptyString, const Error *error=0)=0
virtual void handleSubscriptions(const std::string &id, const JID &service, const SubscriptionMap &subMap, const Error *error=0)=0
virtual void handleUnsubscriptionResult(const std::string &id, const JID &service, const Error *error=0)=0
virtual void handleSubscribers(const std::string &id, const JID &service, const std::string &node, const SubscriptionList &list, const Error *error=0)=0
virtual void handleItemPublication(const std::string &id, const JID &service, const std::string &node, const ItemList &itemList, const Error *error=0)=0
virtual void handleSubscriptionResult(const std::string &id, const JID &service, const std::string &node, const std::string &sid, const JID &jid, const SubscriptionType subType, const Error *error=0)=0
virtual void handleAffiliatesResult(const std::string &id, const JID &service, const std::string &node, const AffiliateList *list, const Error *error=0)=0
virtual void handleAffiliates(const std::string &id, const JID &service, const std::string &node, const AffiliateList *list, const Error *error=0)=0
virtual void handleNodePurge(const std::string &id, const JID &service, const std::string &node, const Error *error=0)=0
virtual void handleItemDeletion(const std::string &id, const JID &service, const std::string &node, const ItemList &itemList, const Error *error=0)=0
virtual void handleNodeCreation(const std::string &id, const JID &service, const std::string &node, const Error *error=0)=0
virtual void handleNodeConfig(const std::string &id, const JID &service, const std::string &node, const DataForm *config, const Error *error=0)=0
virtual void handleSubscribersResult(const std::string &id, const JID &service, const std::string &node, const SubscriberList *list, const Error *error=0)=0
An implementation/abstraction of Stanza Headers and Internet Metadata (SHIM, XEP-0131).
Definition: shim.h:36
void addExtension(const StanzaExtension *se)
Definition: stanza.cpp:52
const Error * error() const
Definition: stanza.cpp:47
const std::string & id() const
Definition: stanza.h:63
const JID & from() const
Definition: stanza.h:51
const StanzaExtension * findExtension(int type) const
Definition: stanza.cpp:57
An abstraction of a subscription stanza.
Definition: subscription.h:32
SubscriptionObject
Definition: pubsub.h:97
@ SubscriptionItems
Definition: pubsub.h:99
@ SubscriptionNodes
Definition: pubsub.h:98
SubscriptionType
Definition: pubsub.h:61
@ SubscriptionInvalid
Definition: pubsub.h:75
@ NodeCollection
Definition: pubsub.h:39
void clearList(std::list< T * > &L)
Definition: util.h:152
The namespace for the gloox library.
Definition: adhoc.cpp:28
std::list< Tag * > TagList
Definition: tag.h:26
const std::string EmptyString
Definition: gloox.cpp:124
std::list< const Tag * > ConstTagList
Definition: tag.h:36
const std::string XMLNS_PUBSUB_OWNER
Definition: gloox.cpp:80
@ ExtPubSubOwner
@ TypeSubmit
Definition: dataform.h:37
const std::string XMLNS_PUBSUB
Definition: gloox.cpp:77