1 /*
2   Copyright (c) 2007-2019 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 
34     /**
35      * Finds the associated PubSubFeature for a feature tag 'type' attribute,
36      * as received from a disco info query on a pubsub service (XEP-0060 sect. 10).
37      * @param feat Feature string to search for.
38      * @return the associated PubSubFeature.
39      */
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 
subscriptionType(const std::string & subscription)83     static inline SubscriptionType subscriptionType( const std::string& subscription )
84     {
85       return static_cast<SubscriptionType>( util::lookup( subscription, subscriptionValues ) );
86     }
87 
subscriptionValue(SubscriptionType subscription)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 
affiliationType(const std::string & affiliation)97     static inline AffiliationType affiliationType( const std::string& affiliation )
98     {
99       return static_cast<AffiliationType>( util::lookup( affiliation, affiliationValues ) );
100     }
101 
affiliationValue(AffiliationType affiliation)102     static inline const std::string affiliationValue( AffiliationType affiliation )
103     {
104       return util::lookup( affiliation, affiliationValues );
105     }
106 
107     // ---- Manager::PubSubOwner ----
PubSubOwner(TrackContext context)108     Manager::PubSubOwner::PubSubOwner( TrackContext context )
109       : StanzaExtension( ExtPubSubOwner ), m_ctx( context ), m_form( 0 )
110     {
111     }
112 
PubSubOwner(const Tag * tag)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 
~PubSubOwner()187     Manager::PubSubOwner::~PubSubOwner()
188     {
189       delete m_form;
190     }
191 
filterString() const192     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 
tag() const198     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 ----
PubSub(TrackContext context)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 
PubSub(const Tag * tag)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 
~PubSub()433     Manager::PubSub::~PubSub()
434     {
435       delete m_options.df;
436       util::clearList( m_items );
437     }
438 
filterString() const439     const std::string& Manager::PubSub::filterString() const
440     {
441       static const std::string filter = "/iq/pubsub[@xmlns='" + XMLNS_PUBSUB + "']";
442       return filter;
443     }
444 
tag() const445     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 
clone() const558     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 ----
Manager(ClientBase * parent)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 
getSubscriptionsOrAffiliations(const JID & service,ResultHandler * handler,TrackContext context)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 
subscribe(const JID & service,const std::string & node,ResultHandler * handler,const JID & jid,SubscriptionObject type,int depth,const std::string & expire)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 
subscribe(const JID & service,const std::string & node,ResultHandler * handler,const JID & jid,DataForm * options)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 
unsubscribe(const JID & service,const std::string & node,const std::string & subid,ResultHandler * handler,const JID & jid)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 
subscriptionOptions(TrackContext context,const JID & service,const JID & jid,const std::string & node,ResultHandler * handler,DataForm * df,const std::string & subid)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 
requestItems(const JID & service,const std::string & node,const std::string & subid,int maxItems,ResultHandler * handler)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 
requestItems(const JID & service,const std::string & node,const std::string & subid,const ItemList & items,ResultHandler * handler)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 
publishItem(const JID & service,const std::string & node,ItemList & items,DataForm * options,ResultHandler * handler)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 
deleteItem(const JID & service,const std::string & node,const ItemList & items,bool notify,ResultHandler * handler)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 
createNode(const JID & service,const std::string & node,DataForm * config,ResultHandler * handler)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 
deleteNode(const JID & service,const std::string & node,ResultHandler * handler)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 
getDefaultNodeConfig(const JID & service,NodeType type,ResultHandler * handler)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 
nodeConfig(const JID & service,const std::string & node,DataForm * config,ResultHandler * handler)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 
subscriberList(TrackContext ctx,const JID & service,const std::string & node,const SubscriberList & subList,ResultHandler * handler)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 
affiliateList(TrackContext ctx,const JID & service,const std::string & node,const AffiliateList & affList,ResultHandler * handler)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 
purgeNode(const JID & service,const std::string & node,ResultHandler * handler)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 
removeID(const std::string & id)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 
handleIqID(const IQ & iq,int context)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 
1267