gloox  1.0.1
pubsubmanager.cpp
1 /*
2  Copyright (c) 2007-2012 by Jakob Schroeter <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 (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 (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  ConstTagList l = tag->findTagList( "pubsub/subscriptions/subscription" );
296  if( l.size() )
297  {
298  m_ctx = GetSubscriptionList;
299  ConstTagList::const_iterator it = l.begin();
300  for( ; it != l.end(); ++it )
301  {
302  const std::string& node = (*it)->findAttribute( "node" );
303  const std::string& sub = (*it)->findAttribute( "subscription" );
304  const std::string& subid = (*it)->findAttribute( "subid" );
305  SubscriptionInfo si;
306  si.jid.setJID( (*it)->findAttribute( "jid" ) );
307  si.type = subscriptionType( sub );
308  si.subid = subid;
309  SubscriptionList& lst = m_subscriptionMap[node];
310  lst.push_back( si );
311  }
312  return;
313  }
314  l = tag->findTagList( "pubsub/affiliations/affiliation" );
315  if( l.size() )
316  {
317  m_ctx = GetAffiliationList;
318  ConstTagList::const_iterator it = l.begin();
319  for( ; it != l.end(); ++it )
320  {
321  const std::string& node = (*it)->findAttribute( "node" );
322  const std::string& aff = (*it)->findAttribute( "affiliation" );
323  m_affiliationMap[node] = affiliationType( aff );
324  }
325  return;
326  }
327  const Tag* s = tag->findTag( "pubsub/subscribe" );
328  if( s )
329  {
330  m_ctx = Subscription;
331  m_node = s->findAttribute( "node" );
332  m_jid = s->findAttribute( "jid" );
333  }
334  const Tag* u = tag->findTag( "pubsub/unsubscribe" );
335  if( u )
336  {
337  m_ctx = Unsubscription;
338  m_node = u->findAttribute( "node" );
339  m_jid = u->findAttribute( "jid" );
340  m_subid = u->findAttribute( "subid" );
341  }
342  const Tag* o = tag->findTag( "pubsub/options" );
343  if( o )
344  {
345  if( m_ctx == InvalidContext )
346  {
347  Tag* parent = tag->parent();
348  if( parent && parent->findAttribute("type") == "set" )
349  m_ctx = SetSubscriptionOptions;
350  else
351  m_ctx = GetSubscriptionOptions;
352  }
353  if( m_ctx == SetSubscriptionOptions || m_ctx == GetSubscriptionOptions )
354  {
355  // We set both m_node and m_options.node for
356  // get/set options, since m_options.node is not exposed externally
357  m_node = o->findAttribute( "node" );
358  m_jid.setJID( o->findAttribute( "jid" ) );
359  m_subid = o->findAttribute( "subid" );
360  }
361  m_options.node = o->findAttribute( "node" );
362  m_options.df = new DataForm( o->findChild( "x", "xmlns", XMLNS_X_DATA ) );
363  }
364  const Tag* su = tag->findTag( "pubsub/subscription" );
365  if( su )
366  {
367  SubscriptionInfo si;
368  si.jid.setJID( su->findAttribute( "jid" ) );
369  si.subid = su->findAttribute( "subid" );
370  si.type = subscriptionType( su->findAttribute( "type" ) );
371  SubscriptionList& lst = m_subscriptionMap[su->findAttribute( "node" )];
372  lst.push_back( si );
373  return;
374  }
375  const Tag* i = tag->findTag( "pubsub/items" );
376  if( i )
377  {
378  m_ctx = RequestItems;
379  m_node = i->findAttribute( "node" );
380  m_subid = i->findAttribute( "subid" );
381  m_maxItems = atoi( i->findAttribute( "max_items" ).c_str() );
382  const TagList& l = i->children();
383  TagList::const_iterator it = l.begin();
384  for( ; it != l.end(); ++it )
385  m_items.push_back( new Item( (*it) ) );
386  return;
387  }
388  const Tag* p = tag->findTag( "pubsub/publish" );
389  if( p )
390  {
391  m_ctx = PublishItem;
392  m_node = p->findAttribute( "node" );
393  const TagList& l = p->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* r = tag->findTag( "pubsub/retract" );
400  if( r )
401  {
402  m_ctx = DeleteItem;
403  m_node = r->findAttribute( "node" );
404  m_notify = r->hasAttribute( "notify", "1" ) || r->hasAttribute( "notify", "true" );
405  const TagList& l = r->children();
406  TagList::const_iterator it = l.begin();
407  for( ; it != l.end(); ++it )
408  m_items.push_back( new Item( (*it) ) );
409  return;
410  }
411  const Tag* c = tag->findTag( "pubsub/create" );
412  if( c )
413  {
414  m_ctx = CreateNode;
415  m_node = c->findAttribute( "node" );
416  const Tag* config = tag->findTag( "pubsub/configure" );
417  if( config && config->hasChild( "x", XMLNS_X_DATA ) )
418  m_options.df = new DataForm( config->findChild( "x", XMLNS_X_DATA ) );
419  }
420  }
421 
422  Manager::PubSub::~PubSub()
423  {
424  delete m_options.df;
425  util::clearList( m_items );
426  }
427 
428  const std::string& Manager::PubSub::filterString() const
429  {
430  static const std::string filter = "/iq/pubsub[@xmlns='" + XMLNS_PUBSUB + "']";
431  return filter;
432  }
433 
434  Tag* Manager::PubSub::tag() const
435  {
436  if( m_ctx == InvalidContext )
437  return 0;
438 
439  Tag* t = new Tag( "pubsub" );
440  t->setXmlns( XMLNS_PUBSUB );
441 
442  if( m_ctx == GetSubscriptionList )
443  {
444  Tag* sub = new Tag( t, "subscriptions" );
445  SubscriptionMap::const_iterator it = m_subscriptionMap.begin();
446  for( ; it != m_subscriptionMap.end(); ++it )
447  {
448  const SubscriptionList& lst = (*it).second;
449  SubscriptionList::const_iterator it2 = lst.begin();
450  for( ; it2 != lst.end(); ++it2 )
451  {
452  Tag* s = new Tag( sub, "subscription" );
453  s->addAttribute( "node", (*it).first );
454  s->addAttribute( "jid", (*it2).jid );
455  s->addAttribute( "subscription", subscriptionValue( (*it2).type ) );
456  s->addAttribute( "sid", (*it2).subid );
457  }
458  }
459  }
460  else if( m_ctx == GetAffiliationList )
461  {
462 
463  Tag* aff = new Tag( t, "affiliations" );
464  AffiliationMap::const_iterator it = m_affiliationMap.begin();
465  for( ; it != m_affiliationMap.end(); ++it )
466  {
467  Tag* a = new Tag( aff, "affiliation" );
468  a->addAttribute( "node", (*it).first );
469  a->addAttribute( "affiliation", affiliationValue( (*it).second ) );
470  }
471  }
472  else if( m_ctx == Subscription )
473  {
474  Tag* s = new Tag( t, "subscribe" );
475  s->addAttribute( "node", m_node );
476  s->addAttribute( "jid", m_jid.full() );
477  if( m_options.df )
478  {
479  Tag* o = new Tag( t, "options" );
480  o->addChild( m_options.df->tag() );
481  }
482  }
483  else if( m_ctx == Unsubscription )
484  {
485  Tag* u = new Tag( t, "unsubscribe" );
486  u->addAttribute( "node", m_node );
487  u->addAttribute( "jid", m_jid.full() );
488  u->addAttribute( "subid", m_subid );
489  }
490  else if( m_ctx == GetSubscriptionOptions
491  || m_ctx == SetSubscriptionOptions )
492  {
493  Tag* o = new Tag( t, "options" );
494  o->addAttribute( "node", m_options.node );
495  o->addAttribute( "jid", m_jid.full() );
496  if( !m_subid.empty() )
497  o->addAttribute( "subid", m_subid );
498  if( m_options.df )
499  o->addChild( m_options.df->tag() );
500  }
501  else if( m_ctx == RequestItems )
502  {
503  Tag* i = new Tag( t, "items" );
504  i->addAttribute( "node", m_node );
505  if( m_maxItems )
506  i->addAttribute( "max_items", m_maxItems );
507  i->addAttribute( "subid", m_subid );
508  ItemList::const_iterator it = m_items.begin();
509  for( ; it != m_items.end(); ++it )
510  i->addChild( (*it)->tag() );
511  }
512  else if( m_ctx == PublishItem )
513  {
514  Tag* p = new Tag( t, "publish" );
515  p->addAttribute( "node", m_node );
516  ItemList::const_iterator it = m_items.begin();
517  for( ; it != m_items.end(); ++it )
518  p->addChild( (*it)->tag() );
519  if( m_options.df )
520  {
521  Tag* po = new Tag( "publish-options" );
522  po->addChild( m_options.df->tag() );
523  }
524  }
525  else if( m_ctx == DeleteItem )
526  {
527  Tag* r = new Tag( t, "retract" );
528  r->addAttribute( "node", m_node );
529  if( m_notify )
530  r->addAttribute( "notify", "true" );
531  ItemList::const_iterator it = m_items.begin();
532  for( ; it != m_items.end(); ++it )
533  r->addChild( (*it)->tag() );
534  }
535  else if( m_ctx == CreateNode )
536  {
537  Tag* c = new Tag( t, "create" );
538  c->addAttribute( "node", m_node );
539  Tag* config = new Tag( t, "configure" );
540  if( m_options.df )
541  config->addChild( m_options.df->tag() );
542  }
543  return t;
544  }
545 
546  StanzaExtension* Manager::PubSub::clone() const
547  {
548  PubSub* p = new PubSub();
549  p->m_affiliationMap = m_affiliationMap;
550  p->m_subscriptionMap = m_subscriptionMap;
551  p->m_ctx = m_ctx;
552 
553  p->m_options.node = m_options.node;
554  p->m_options.df = m_options.df ? new DataForm( *(m_options.df) ) : 0;
555 
556  p->m_jid = m_jid;
557  p->m_node = m_node;
558  p->m_subid = m_subid;
559  ItemList::const_iterator it = m_items.begin();
560  for( ; it != m_items.end(); ++it )
561  p->m_items.push_back( new Item( *(*it) ) );
562 
563  p->m_maxItems = m_maxItems;
564  p->m_notify = m_notify;
565  return p;
566  }
567  // ---- ~Manager::PubSub ----
568 
569  // ---- Manager ----
571  : m_parent( parent )
572  {
573  if( m_parent )
574  {
575  m_parent->registerStanzaExtension( new PubSub() );
576  m_parent->registerStanzaExtension( new PubSubOwner() );
577  m_parent->registerStanzaExtension( new SHIM() );
578  }
579  }
580 
581  const std::string Manager::getSubscriptionsOrAffiliations( const JID& service,
582  ResultHandler* handler,
583  TrackContext context )
584  {
585  if( !m_parent || !handler || !service || context == InvalidContext )
586  return EmptyString;
587 
588  const std::string& id = m_parent->getID();
589  IQ iq( IQ::Get, service, id );
590  iq.addExtension( new PubSub( context ) );
591 
592  m_trackMapMutex.lock();
593  m_resultHandlerTrackMap[id] = handler;
594  m_trackMapMutex.unlock();
595  m_parent->send( iq, this, context );
596  return id;
597  }
598 
599  const std::string Manager::subscribe( const JID& service,
600  const std::string& node,
601  ResultHandler* handler,
602  const JID& jid,
603  SubscriptionObject type,
604  int depth,
605  const std::string& expire
606  )
607  {
608  if( !m_parent || !handler || !service || node.empty() )
609  return EmptyString;
610 
611  DataForm* options = 0;
612  if( type != SubscriptionNodes || depth != 1 )
613  {
614  options = new DataForm( TypeSubmit );
615  options->addField( DataFormField::TypeHidden, "FORM_TYPE", XMLNS_PUBSUB_SUBSCRIBE_OPTIONS );
616 
617  if( type == SubscriptionItems )
618  options->addField( DataFormField::TypeNone, "pubsub#subscription_type", "items" );
619 
620  if( depth != 1 )
621  {
622  DataFormField* field = options->addField( DataFormField::TypeNone, "pubsub#subscription_depth" );
623  if( depth == 0 )
624  field->setValue( "all" );
625  else
626  field->setValue( util::int2string( depth ) );
627  }
628 
629  if( !expire.empty() )
630  {
631  DataFormField* field = options->addField( DataFormField::TypeNone, "pubsub#expire" );
632  field->setValue( expire );
633  }
634  }
635 
636  return subscribe( service, node, handler, jid, options );
637  }
638 
639  const std::string Manager::subscribe( const JID& service,
640  const std::string& node,
641  ResultHandler* handler,
642  const JID& jid,
643  DataForm* options
644  )
645  {
646  if( !m_parent || !handler || !service || node.empty() )
647  return EmptyString;
648 
649  const std::string& id = m_parent->getID();
650  IQ iq( IQ::Set, service, id );
651  PubSub* ps = new PubSub( Subscription );
652  ps->setJID( jid ? jid : m_parent->jid() );
653  ps->setNode( node );
654  if( options != NULL )
655  ps->setOptions( node, options );
656  iq.addExtension( ps );
657 
658  m_trackMapMutex.lock();
659  m_resultHandlerTrackMap[id] = handler;
660  m_nopTrackMap[id] = node;
661  m_trackMapMutex.unlock();
662  m_parent->send( iq, this, Subscription );
663  return id;
664  }
665 
666  const std::string Manager::unsubscribe( const JID& service,
667  const std::string& node,
668  const std::string& subid,
669  ResultHandler* handler,
670  const JID& jid )
671  {
672  if( !m_parent || !handler || !service )
673  return EmptyString;
674 
675  const std::string& id = m_parent->getID();
676  IQ iq( IQ::Set, service, id );
677  PubSub* ps = new PubSub( Unsubscription );
678  ps->setNode( node );
679  ps->setJID( jid ? jid : m_parent->jid() );
680  ps->setSubscriptionID( subid );
681  iq.addExtension( ps );
682 
683  m_trackMapMutex.lock();
684  m_resultHandlerTrackMap[id] = handler;
685  m_trackMapMutex.unlock();
686  // FIXME? need to track info for handler
687  m_parent->send( iq, this, Unsubscription );
688  return id;
689  }
690 
691  const std::string Manager::subscriptionOptions( TrackContext context,
692  const JID& service,
693  const JID& jid,
694  const std::string& node,
695  ResultHandler* handler,
696  DataForm* df,
697  const std::string& subid )
698  {
699  if( !m_parent || !handler || !service )
700  return EmptyString;
701 
702  const std::string& id = m_parent->getID();
703  IQ iq( df ? IQ::Set : IQ::Get, service, id );
704  PubSub* ps = new PubSub( context );
705  ps->setJID( jid ? jid : m_parent->jid() );
706  if( !subid.empty() )
707  ps->setSubscriptionID( subid );
708  ps->setOptions( node, df );
709  iq.addExtension( ps );
710 
711  m_trackMapMutex.lock();
712  m_resultHandlerTrackMap[id] = handler;
713  m_trackMapMutex.unlock();
714  m_parent->send( iq, this, context );
715  return id;
716  }
717 
718  const std::string Manager::requestItems( const JID& service,
719  const std::string& node,
720  const std::string& subid,
721  int maxItems,
722  ResultHandler* handler )
723  {
724  if( !m_parent || !service || !handler )
725  return EmptyString;
726 
727  const std::string& id = m_parent->getID();
728  IQ iq( IQ::Get, service, id );
729  PubSub* ps = new PubSub( RequestItems );
730  ps->setNode( node );
731  ps->setSubscriptionID( subid );
732  ps->setMaxItems( maxItems );
733  iq.addExtension( ps );
734 
735  m_trackMapMutex.lock();
736  m_resultHandlerTrackMap[id] = handler;
737  m_trackMapMutex.unlock();
738  m_parent->send( iq, this, RequestItems );
739  return id;
740  }
741 
742  const std::string Manager::requestItems( const JID& service,
743  const std::string& node,
744  const std::string& subid,
745  const ItemList& items,
746  ResultHandler* handler )
747  {
748  if( !m_parent || !service || !handler )
749  return EmptyString;
750 
751  const std::string& id = m_parent->getID();
752  IQ iq( IQ::Get, service, id );
753  PubSub* ps = new PubSub( RequestItems );
754  ps->setNode( node );
755  ps->setSubscriptionID( subid );
756  ps->setItems( items );
757  iq.addExtension( ps );
758 
759  m_trackMapMutex.lock();
760  m_resultHandlerTrackMap[id] = handler;
761  m_trackMapMutex.unlock();
762  m_parent->send( iq, this, RequestItems );
763  return id;
764  }
765 
766  const std::string Manager::publishItem( const JID& service,
767  const std::string& node,
768  ItemList& items,
769  DataForm* options,
770  ResultHandler* handler )
771  {
772  if( !m_parent || !handler )
773  {
774  util::clearList( items );
775  return EmptyString;
776  }
777 
778  const std::string& id = m_parent->getID();
779  IQ iq( IQ::Set, service, id );
780  PubSub* ps = new PubSub( PublishItem );
781  ps->setNode( node );
782  ps->setItems( items );
783  ps->setOptions( EmptyString, options );
784  iq.addExtension( ps );
785 
786  m_trackMapMutex.lock();
787  m_resultHandlerTrackMap[id] = handler;
788  m_trackMapMutex.unlock();
789  m_parent->send( iq, this, PublishItem );
790  return id;
791  }
792 
793  const std::string Manager::deleteItem( const JID& service,
794  const std::string& node,
795  const ItemList& items,
796  bool notify,
797  ResultHandler* handler )
798  {
799  if( !m_parent || !handler || !service )
800  return EmptyString;
801 
802  const std::string& id = m_parent->getID();
803  IQ iq( IQ::Set, service, id );
804  PubSub* ps = new PubSub( DeleteItem );
805  ps->setNode( node );
806  ps->setItems( items );
807  ps->setNotify( notify );
808  iq.addExtension( ps );
809 
810  m_trackMapMutex.lock();
811  m_resultHandlerTrackMap[id] = handler;
812  m_trackMapMutex.unlock();
813  m_parent->send( iq, this, DeleteItem );
814  return id;
815  }
816 
817  const std::string Manager::createNode( const JID& service,
818  const std::string& node,
819  DataForm* config,
820  ResultHandler* handler )
821  {
822  if( !m_parent || !handler || !service || node.empty() )
823  return EmptyString;
824 
825  const std::string& id = m_parent->getID();
826  IQ iq( IQ::Set, service, id );
827  PubSub* ps = new PubSub( CreateNode );
828  ps->setNode( node );
829  ps->setOptions( EmptyString, config );
830  iq.addExtension( ps );
831 
832  m_trackMapMutex.lock();
833  m_nopTrackMap[id] = node;
834  m_resultHandlerTrackMap[id] = handler;
835  m_trackMapMutex.unlock();
836  m_parent->send( iq, this, CreateNode );
837  return id;
838  }
839 
840  const std::string Manager::deleteNode( const JID& service,
841  const std::string& node,
842  ResultHandler* handler )
843  {
844  if( !m_parent || !handler || !service || node.empty() )
845  return EmptyString;
846 
847  const std::string& id = m_parent->getID();
848  IQ iq( IQ::Set, service, id );
849  PubSubOwner* pso = new PubSubOwner( DeleteNode );
850  pso->setNode( node );
851  iq.addExtension( pso );
852 
853  m_trackMapMutex.lock();
854  m_nopTrackMap[id] = node;
855  m_resultHandlerTrackMap[id] = handler;
856  m_trackMapMutex.unlock();
857  m_parent->send( iq, this, DeleteNode );
858  return id;
859  }
860 
861  const std::string Manager::getDefaultNodeConfig( const JID& service,
862  NodeType type,
863  ResultHandler* handler )
864  {
865  if( !m_parent || !handler || !service )
866  return EmptyString;
867 
868  const std::string& id = m_parent->getID();
869  IQ iq( IQ::Get, service, id );
870  PubSubOwner* pso = new PubSubOwner( DefaultNodeConfig );
871  if( type == NodeCollection )
872  {
873  DataForm* df = new DataForm( TypeSubmit );
874  df->addField( DataFormField::TypeHidden, "FORM_TYPE", XMLNS_PUBSUB_NODE_CONFIG );
875  df->addField( DataFormField::TypeNone, "pubsub#node_type", "collection" );
876  pso->setConfig( df );
877  }
878  iq.addExtension( pso );
879 
880  m_trackMapMutex.lock();
881  m_resultHandlerTrackMap[id] = handler;
882  m_trackMapMutex.unlock();
883  m_parent->send( iq, this, DefaultNodeConfig );
884  return id;
885  }
886 
887  const std::string Manager::nodeConfig( const JID& service,
888  const std::string& node,
889  DataForm* config,
890  ResultHandler* handler )
891  {
892  if( !m_parent || !handler || !service || node.empty() )
893  return EmptyString;
894 
895  const std::string& id = m_parent->getID();
896  IQ iq( config ? IQ::Set : IQ::Get, service, id );
897  PubSubOwner* pso = new PubSubOwner( config ? SetNodeConfig : GetNodeConfig );
898  pso->setNode( node );
899  if( config )
900  pso->setConfig( config );
901  iq.addExtension( pso );
902 
903  m_trackMapMutex.lock();
904  m_resultHandlerTrackMap[id] = handler;
905  m_trackMapMutex.unlock();
906  m_parent->send( iq, this, config ? SetNodeConfig : GetNodeConfig );
907  return id;
908  }
909 
910  const std::string Manager::subscriberList( TrackContext ctx,
911  const JID& service,
912  const std::string& node,
913  const SubscriberList& subList,
914  ResultHandler* handler )
915  {
916  if( !m_parent || !handler || !service || node.empty() )
917  return EmptyString;
918 
919  const std::string& id = m_parent->getID();
920  IQ iq( ctx == SetSubscriberList ? IQ::Set : IQ::Get, service, id );
921  PubSubOwner* pso = new PubSubOwner( ctx );
922  pso->setNode( node );
923  pso->setSubscriberList( subList );
924  iq.addExtension( pso );
925 
926  m_trackMapMutex.lock();
927  m_nopTrackMap[id] = node;
928  m_resultHandlerTrackMap[id] = handler;
929  m_trackMapMutex.unlock();
930  m_parent->send( iq, this, ctx );
931  return id;
932  }
933 
934  const std::string Manager::affiliateList( TrackContext ctx,
935  const JID& service,
936  const std::string& node,
937  const AffiliateList& affList,
938  ResultHandler* handler )
939  {
940  if( !m_parent || !handler || !service || node.empty() )
941  return EmptyString;
942 
943  const std::string& id = m_parent->getID();
944  IQ iq( ctx == SetAffiliateList ? IQ::Set : IQ::Get, service, id );
945  PubSubOwner* pso = new PubSubOwner( ctx );
946  pso->setNode( node );
947  pso->setAffiliateList( affList );
948  iq.addExtension( pso );
949 
950  m_trackMapMutex.lock();
951  m_nopTrackMap[id] = node;
952  m_resultHandlerTrackMap[id] = handler;
953  m_trackMapMutex.unlock();
954  m_parent->send( iq, this, ctx );
955  return id;
956  }
957 
958  const std::string Manager::purgeNode( const JID& service,
959  const std::string& node,
960  ResultHandler* handler )
961  {
962  if( !m_parent || !handler || !service || node.empty() )
963  return EmptyString;
964 
965  const std::string& id = m_parent->getID();
966  IQ iq( IQ::Set, service, id );
967  PubSubOwner* pso = new PubSubOwner( PurgeNodeItems );
968  pso->setNode( node );
969  iq.addExtension( pso );
970 
971  m_trackMapMutex.lock();
972  m_nopTrackMap[id] = node;
973  m_resultHandlerTrackMap[id] = handler;
974  m_trackMapMutex.unlock();
975  m_parent->send( iq, this, PurgeNodeItems );
976  return id;
977  }
978 
979  bool Manager::removeID( const std::string& id )
980  {
981  m_trackMapMutex.lock();
982  ResultHandlerTrackMap::iterator ith = m_resultHandlerTrackMap.find( id );
983  if( ith == m_resultHandlerTrackMap.end() )
984  {
985  m_trackMapMutex.unlock();
986  return false;
987  }
988  m_resultHandlerTrackMap.erase( ith );
989  m_trackMapMutex.unlock();
990  return true;
991  }
992 
993  void Manager::handleIqID( const IQ& iq, int context )
994  {
995  const JID& service = iq.from();
996  const std::string& id = iq.id();
997 
998  m_trackMapMutex.lock();
999  ResultHandlerTrackMap::iterator ith = m_resultHandlerTrackMap.find( id );
1000  if( ith == m_resultHandlerTrackMap.end() )
1001  {
1002  m_trackMapMutex.unlock();
1003  return;
1004  }
1005  ResultHandler* rh = (*ith).second;
1006  m_resultHandlerTrackMap.erase( ith );
1007  m_trackMapMutex.unlock();
1008 
1009  switch( iq.subtype() )
1010  {
1011  case IQ::Error:
1012  case IQ::Result:
1013  {
1014  const Error* error = iq.error();
1015  switch( context )
1016  {
1017  case Subscription:
1018  {
1019  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1020  if( !ps )
1021  return;
1022  SubscriptionMap sm = ps->subscriptions();
1023  if( !sm.empty() )
1024  {
1025  SubscriptionMap::const_iterator it = sm.begin();
1026  const SubscriptionList& lst = (*it).second;
1027  if( lst.size() == 1 )
1028  {
1029  SubscriptionList::const_iterator it2 = lst.begin();
1030  rh->handleSubscriptionResult( id, service, (*it).first, (*it2).subid, (*it2).jid,
1031  (*it2).type, error );
1032  }
1033  }
1034  break;
1035  }
1036  case Unsubscription:
1037  {
1038  rh->handleUnsubscriptionResult( iq.id(), service, error );
1039  break;
1040  }
1041  case GetSubscriptionList:
1042  {
1043  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1044  if( !ps )
1045  return;
1046 
1047  rh->handleSubscriptions( id, service,
1048  ps->subscriptions(),
1049  error );
1050  break;
1051  }
1052  case GetAffiliationList:
1053  {
1054  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1055  if( !ps )
1056  return;
1057 
1058  rh->handleAffiliations( id, service,
1059  ps->affiliations(),
1060  error );
1061  break;
1062  }
1063  case RequestItems:
1064  {
1065  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1066  if( !ps )
1067  return;
1068 
1069  rh->handleItems( id, service, ps->node(),
1070  ps->items(), error );
1071  break;
1072  }
1073  case PublishItem:
1074  {
1075  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1076  rh->handleItemPublication( id, service, "",
1077  ps ? ps->items() : ItemList(),
1078  error );
1079  break;
1080  }
1081  case DeleteItem:
1082  {
1083  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1084  if( ps )
1085  {
1086  rh->handleItemDeletion( id, service,
1087  ps->node(),
1088  ps->items(),
1089  error );
1090  }
1091  break;
1092  }
1093  case DefaultNodeConfig:
1094  {
1095  const PubSubOwner* pso = iq.findExtension<PubSubOwner>( ExtPubSubOwner );
1096  if( pso )
1097  {
1098  rh->handleDefaultNodeConfig( id, service,
1099  pso->config(),
1100  error );
1101  }
1102  break;
1103  }
1104  case GetSubscriptionOptions:
1105  case GetSubscriberList:
1106  case SetSubscriberList:
1107  case GetAffiliateList:
1108  case SetAffiliateList:
1109  case GetNodeConfig:
1110  case SetNodeConfig:
1111  case CreateNode:
1112  case DeleteNode:
1113  case PurgeNodeItems:
1114  {
1115  switch( context )
1116  {
1117  case GetSubscriptionOptions:
1118  {
1119  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1120  if( ps )
1121  {
1122  rh->handleSubscriptionOptions( id, service,
1123  ps->jid(),
1124  ps->node(),
1125  ps->options(),
1126  ps->subscriptionID(),
1127  error );
1128  }
1129  break;
1130  }
1131 // case GetSubscriberList:
1132 // {
1133 // const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1134 // if( ps )
1135 // {
1136 // rh->handleSubscribers( service, ps->node(), ps->subscriptions() );
1137 // }
1138 // break;
1139 // }
1140  case SetSubscriptionOptions:
1141  case SetSubscriberList:
1142  case SetAffiliateList:
1143  case SetNodeConfig:
1144  case CreateNode:
1145  case DeleteNode:
1146  case PurgeNodeItems:
1147  {
1148  m_trackMapMutex.lock();
1149  NodeOperationTrackMap::iterator it = m_nopTrackMap.find( id );
1150  if( it != m_nopTrackMap.end() )
1151  {
1152  const std::string& node = (*it).second;
1153  switch( context )
1154  {
1155  case SetSubscriptionOptions:
1156  {
1157  const PubSub* ps = iq.findExtension<PubSub>( ExtPubSub );
1158  if( ps )
1159  {
1160  rh->handleSubscriptionOptionsResult( id, service,
1161  ps->jid(),
1162  node,
1163  ps->subscriptionID(),
1164  error );
1165  }
1166  else
1167  {
1168  rh->handleSubscriptionOptionsResult( id, service, JID( /* FIXME */ ), node, /* FIXME */ EmptyString, error );
1169  }
1170  break;
1171  }
1172  case SetSubscriberList:
1173  rh->handleSubscribersResult( id, service, node, 0, error );
1174  break;
1175  case SetAffiliateList:
1176  rh->handleAffiliatesResult( id, service, node, 0, error );
1177  break;
1178  case SetNodeConfig:
1179  rh->handleNodeConfigResult( id, service, node, error );
1180  break;
1181  case CreateNode:
1182  rh->handleNodeCreation( id, service, node, error );
1183  break;
1184  case DeleteNode:
1185  rh->handleNodeDeletion( id, service, node, error );
1186  break;
1187  case PurgeNodeItems:
1188  rh->handleNodePurge( id, service, node, error );
1189  break;
1190  }
1191  m_nopTrackMap.erase( it );
1192  }
1193  m_trackMapMutex.unlock();
1194  break;
1195  }
1196  case GetAffiliateList:
1197  {
1198 // const PubSub
1199 
1200  /* const TagList& affiliates = query->children();
1201  AffiliateList affList;
1202  TagList::const_iterator it = affiliates.begin();
1203  for( ; it != affiliates.end(); ++it )
1204  {
1205  Affiliate aff( (*it)->findAttribute( "jid" ),
1206  affiliationType( (*it)->findAttribute( "affiliation" ) ) );
1207  affList.push_back( aff );
1208  }
1209  rh->handleAffiliates( service, query->findAttribute( "node" ), &affList );
1210  */
1211  break;
1212  }
1213  case GetNodeConfig:
1214  {
1215  const PubSubOwner* pso = iq.findExtension<PubSubOwner>( ExtPubSubOwner );
1216  if( pso )
1217  {
1218  rh->handleNodeConfig( id, service,
1219  pso->node(),
1220  pso->config(),
1221  error );
1222  }
1223  break;
1224  }
1225  default:
1226  break;
1227  }
1228 
1229  break;
1230  }
1231  }
1232  break;
1233  }
1234  default:
1235  break;
1236  }
1237 
1238  }
1239 
1240  }
1241 
1242 }
1243