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