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