gloox 1.0

disco.cpp

00001 /*
00002   Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
00003   This file is part of the gloox library. http://camaya.net/gloox
00004 
00005   This software is distributed under a license. The full license
00006   agreement can be found in the file LICENSE in this distribution.
00007   This software may not be copied, modified, sold or distributed
00008   other than expressed in the named license agreement.
00009 
00010   This software is distributed without any warranty.
00011 */
00012 
00013 
00014 #include "disco.h"
00015 #include "discohandler.h"
00016 #include "dataform.h"
00017 #include "error.h"
00018 #include "clientbase.h"
00019 #include "disconodehandler.h"
00020 #include "softwareversion.h"
00021 #include "util.h"
00022 
00023 
00024 namespace gloox
00025 {
00026 
00027   // ---- Disco::Identity ----
00028   Disco::Identity::Identity( const std::string& category,
00029                              const std::string& type,
00030                              const std::string& name )
00031     : m_category( category ), m_type( type ), m_name( name )
00032   {
00033   }
00034 
00035   Disco::Identity::Identity( const Tag* tag )
00036   {
00037     if( !tag || tag->name() != "identity" )
00038       return;
00039 
00040     m_category = tag->findAttribute( "category" );
00041     m_type = tag->findAttribute( "type" );
00042     m_name = tag->findAttribute( "name" );
00043   }
00044 
00045   Disco::Identity::Identity( const Identity& id )
00046     : m_category( id.m_category ), m_type( id.m_type ), m_name( id.m_name )
00047   {
00048   }
00049 
00050   Disco::Identity::~Identity()
00051   {
00052   }
00053 
00054   Tag* Disco::Identity::tag() const
00055   {
00056     if( m_category.empty() || m_type.empty() )
00057       return 0;
00058 
00059     Tag* i = new Tag( "identity" );
00060     i->addAttribute( "category", m_category );
00061     i->addAttribute( "type", m_type );
00062 
00063     if( !m_name.empty() )
00064       i->addAttribute( "name", m_name );
00065 
00066     return i;
00067   }
00068   // ---- ~Disco::Identity ----
00069 
00070   // ---- Disco::Info ----
00071   Disco::Info::Info( const std::string& node, bool defaultFeatures )
00072     : StanzaExtension( ExtDiscoInfo ), m_node( node ), m_form( 0 )
00073   {
00074     if( defaultFeatures )
00075     {
00076       m_features.push_back( XMLNS_DISCO_INFO );
00077       m_features.push_back( XMLNS_DISCO_ITEMS );
00078     }
00079   }
00080 
00081   Disco::Info::Info( const Tag* tag )
00082     : StanzaExtension( ExtDiscoInfo ), m_form( 0 )
00083   {
00084     if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_INFO )
00085       return;
00086 
00087     m_node = tag->findAttribute( "node" );
00088 
00089     const TagList& l = tag->children();
00090     TagList::const_iterator it = l.begin();
00091     for( ; it != l.end(); ++it )
00092     {
00093       const std::string& name = (*it)->name();
00094       if( name == "identity" )
00095         m_identities.push_back( new Identity( (*it) ) );
00096       else if( name == "feature" && (*it)->hasAttribute( "var" ) )
00097         m_features.push_back( (*it)->findAttribute( "var" ) );
00098       else if( !m_form && name == "x" && (*it)->xmlns() == XMLNS_X_DATA )
00099         m_form = new DataForm( (*it) );
00100     }
00101   }
00102 
00103   Disco::Info::Info( const Info& info )
00104     : StanzaExtension( ExtDiscoInfo ), m_node( info.m_node ), m_features( info.m_features ),
00105       m_identities( info.m_identities ),  m_form( info.m_form ? new DataForm( *(info.m_form) ) : 0 )
00106   {
00107   }
00108 
00109   Disco::Info::~Info()
00110   {
00111     delete m_form;
00112     util::clearList( m_identities );
00113   }
00114 
00115   void Disco::Info::setForm( DataForm* form )
00116   {
00117     delete m_form;
00118     m_form = form;
00119   }
00120 
00121   bool Disco::Info::hasFeature( const std::string& feature ) const
00122   {
00123     StringList::const_iterator it = m_features.begin();
00124     for( ; it != m_features.end() && (*it) != feature; ++it )
00125       ;
00126     return it != m_features.end();
00127   }
00128 
00129   const std::string& Disco::Info::filterString() const
00130   {
00131     static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_INFO + "']";
00132     return filter;
00133   }
00134 
00135   Tag* Disco::Info::tag() const
00136   {
00137     Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_INFO );
00138 
00139     if( !m_node.empty() )
00140       t->addAttribute( "node", m_node );
00141 
00142     IdentityList::const_iterator it_i = m_identities.begin();
00143     for( ; it_i != m_identities.end(); ++it_i )
00144       t->addChild( (*it_i)->tag() );
00145 
00146     StringList::const_iterator it_f = m_features.begin();
00147     for( ; it_f != m_features.end(); ++it_f )
00148       new Tag( t, "feature", "var", (*it_f) );
00149 
00150     if( m_form )
00151       t->addChild( m_form->tag() );
00152 
00153     return t;
00154   }
00155   // ---- ~Disco::Info ----
00156 
00157   // ---- Disco::Item ----
00158   Disco::Item::Item( const Tag* tag )
00159   {
00160     if( !tag || tag->name() != "item" )
00161       return;
00162 
00163     m_jid = tag->findAttribute( "jid" );
00164     m_node = tag->findAttribute( "node" );
00165     m_name = tag->findAttribute( "name" );
00166   }
00167 
00168   Tag* Disco::Item::tag() const
00169   {
00170     if( !m_jid )
00171       return 0;
00172 
00173     Tag* i = new Tag( "item" );
00174     i->addAttribute( "jid", m_jid.full() );
00175 
00176     if( !m_node.empty() )
00177       i->addAttribute( "node", m_node );
00178     if( !m_name.empty() )
00179       i->addAttribute( "name", m_name );
00180 
00181     return i;
00182   }
00183   // ---- ~Disco::Item ----
00184 
00185   // ---- Disco::Items ----
00186   Disco::Items::Items( const std::string& node )
00187     : StanzaExtension( ExtDiscoItems ), m_node( node )
00188   {
00189   }
00190 
00191   Disco::Items::Items( const Tag* tag )
00192   : StanzaExtension( ExtDiscoItems )
00193   {
00194     if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_ITEMS )
00195       return;
00196 
00197     m_node = tag->findAttribute( "node" );
00198 
00199     const TagList& l = tag->children();
00200     TagList::const_iterator it = l.begin();
00201     for( ; it != l.end(); ++it )
00202     {
00203       const std::string& name = (*it)->name();
00204       if( name == "item" )
00205         m_items.push_back( new Item( (*it) ) );
00206     }
00207   }
00208 
00209   Disco::Items::~Items()
00210   {
00211     util::clearList( m_items );
00212   }
00213 
00214   void Disco::Items::setItems( const ItemList& items )
00215   {
00216     util::clearList( m_items );
00217     m_items = items;
00218   }
00219 
00220 
00221   const std::string& Disco::Items::filterString() const
00222   {
00223     static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_ITEMS + "']";
00224     return filter;
00225   }
00226 
00227   Tag* Disco::Items::tag() const
00228   {
00229     Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_ITEMS );
00230 
00231     if( !m_node.empty() )
00232       t->addAttribute( "node", m_node );
00233 
00234     ItemList::const_iterator it_i = m_items.begin();
00235     for( ; it_i != m_items.end(); ++it_i )
00236       t->addChild( (*it_i)->tag() );
00237 
00238     return t;
00239   }
00240   // ---- ~Disco::Items ----
00241 
00242   // ---- Disco ----
00243   Disco::Disco( ClientBase* parent )
00244     : m_parent( parent ), m_form( 0 )
00245   {
00246     addFeature( XMLNS_VERSION );
00247 //     addFeature( XMLNS_DISCO_INFO ); //handled by Disco::Info now
00248 //     addFeature( XMLNS_DISCO_ITEMS ); //handled by Disco::Info now
00249     if( m_parent )
00250     {
00251       m_parent->registerIqHandler( this, ExtDiscoInfo );
00252       m_parent->registerIqHandler( this, ExtDiscoItems );
00253       m_parent->registerIqHandler( this, ExtVersion );
00254       m_parent->registerStanzaExtension( new Disco::Info() );
00255       m_parent->registerStanzaExtension( new Disco::Items() );
00256       m_parent->registerStanzaExtension( new SoftwareVersion() );
00257     }
00258   }
00259 
00260   Disco::~Disco()
00261   {
00262     util::clearList( m_identities );
00263     delete m_form;
00264 
00265     if( m_parent )
00266     {
00267       m_parent->removeIqHandler( this, ExtDiscoInfo );
00268       m_parent->removeIqHandler( this, ExtDiscoItems );
00269       m_parent->removeIqHandler( this, ExtVersion );
00270       m_parent->removeStanzaExtension( ExtDiscoInfo );
00271       m_parent->removeStanzaExtension( ExtDiscoItems );
00272       m_parent->removeStanzaExtension( ExtVersion );
00273       m_parent->removeIDHandler( this );
00274     }
00275   }
00276 
00277   void Disco::setForm( DataForm* form )
00278   {
00279     delete m_form;
00280     m_form = form;
00281   }
00282 
00283   bool Disco::handleIq( const IQ& iq )
00284   {
00285     switch( iq.subtype() )
00286     {
00287       case IQ::Get:
00288       {
00289         IQ re( IQ::Result, iq.from(), iq.id() );
00290         re.setFrom( iq.to() );
00291 
00292         const SoftwareVersion* sv = iq.findExtension<SoftwareVersion>( ExtVersion );
00293         if( sv )
00294         {
00295           re.addExtension( new SoftwareVersion( m_versionName, m_versionVersion, m_versionOs ) );
00296           m_parent->send( re );
00297           return true;
00298         }
00299 
00300         const Info *info = iq.findExtension<Info>( ExtDiscoInfo );
00301         if( info )
00302         {
00303           Info *i = new Info( EmptyString, true );
00304           if( !info->node().empty() )
00305           {
00306             i->setNode( info->node() );
00307             IdentityList identities;
00308             StringList features;
00309             DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( info->node() );
00310             if( it == m_nodeHandlers.end() )
00311             {
00312               delete i;
00313               IQ re( IQ::Error, iq.from(), iq.id() );
00314               re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) );
00315               m_parent->send( re );
00316               return true;
00317             }
00318             else
00319             {
00320               DiscoNodeHandlerList::const_iterator in = (*it).second.begin();
00321               for( ; in != (*it).second.end(); ++in )
00322               {
00323                 IdentityList il = (*in)->handleDiscoNodeIdentities( iq.from(), info->node() );
00324                 il.sort(); // needed on win32
00325                 identities.merge( il );
00326                 StringList fl = (*in)->handleDiscoNodeFeatures( iq.from(), info->node() );
00327                 fl.sort();  // needed on win32
00328                 features.merge( fl );
00329               }
00330             }
00331             i->setIdentities( identities );
00332             i->setFeatures( features );
00333           }
00334           else
00335           {
00336             IdentityList il;
00337             IdentityList::const_iterator it = m_identities.begin();
00338             for( ; it != m_identities.end(); ++it )
00339             {
00340               il.push_back( new Identity( *(*it) ) );
00341             }
00342             i->setIdentities( il );
00343             i->setFeatures( m_features );
00344             if( m_form )
00345               i->setForm( new DataForm( *m_form ) );
00346           }
00347 
00348           re.addExtension( i );
00349           m_parent->send( re );
00350           return true;
00351         }
00352 
00353         const Items *items = iq.findExtension<Items>( ExtDiscoItems );
00354         if( items )
00355         {
00356           Items *i = new Items( items->node() );
00357           if( !items->node().empty() )
00358           {
00359             DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( items->node() );
00360             if( it == m_nodeHandlers.end() )
00361             {
00362               delete i;
00363               IQ re( IQ::Error, iq.from(), iq.id() );
00364               re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) );
00365               m_parent->send( re );
00366               return true;
00367             }
00368             else
00369             {
00370               ItemList itemlist;
00371               DiscoNodeHandlerList::const_iterator in = (*it).second.begin();
00372               for( ; in != (*it).second.end(); ++in )
00373               {
00374                 ItemList il = (*in)->handleDiscoNodeItems( iq.from(), iq.to(), items->node() );
00375                 il.sort(); // needed on win32
00376                 itemlist.merge( il );
00377               }
00378               i->setItems( itemlist );
00379             }
00380           }
00381 
00382           re.addExtension( i );
00383           m_parent->send( re );
00384           return true;
00385         }
00386         break;
00387       }
00388 
00389       case IQ::Set:
00390       {
00391         bool res = false;
00392         DiscoHandlerList::const_iterator it = m_discoHandlers.begin();
00393         for( ; it != m_discoHandlers.end(); ++it )
00394         {
00395           if( (*it)->handleDiscoSet( iq ) )
00396             res = true;
00397         }
00398         return res;
00399         break;
00400       }
00401 
00402       default:
00403         break;
00404     }
00405     return false;
00406   }
00407 
00408   void Disco::handleIqID( const IQ& iq, int context )
00409   {
00410     DiscoHandlerMap::iterator it = m_track.find( iq.id() );
00411     if( it != m_track.end() && (*it).second.dh )
00412     {
00413       switch( iq.subtype() )
00414       {
00415         case IQ::Result:
00416           switch( context )
00417           {
00418             case GetDiscoInfo:
00419             {
00420               const Info* di = iq.findExtension<Info>( ExtDiscoInfo );
00421               if( di )
00422                 (*it).second.dh->handleDiscoInfo( iq.from(), *di, (*it).second.context );
00423               break;
00424             }
00425             case GetDiscoItems:
00426             {
00427               const Items* di = iq.findExtension<Items>( ExtDiscoItems );
00428               if( di )
00429                 (*it).second.dh->handleDiscoItems( iq.from(), *di, (*it).second.context );
00430               break;
00431             }
00432           }
00433           break;
00434 
00435         case IQ::Error:
00436         {
00437           (*it).second.dh->handleDiscoError( iq.from(), iq.error(), (*it).second.context );
00438           break;
00439         }
00440 
00441         default:
00442           break;
00443       }
00444 
00445       m_track.erase( it );
00446     }
00447   }
00448 
00449   void Disco::getDisco( const JID& to, const std::string& node, DiscoHandler* dh, int context,
00450                         IdType idType, const std::string& tid )
00451   {
00452     const std::string& id = tid.empty() ? m_parent->getID() : tid;
00453 
00454     IQ iq( IQ::Get, to, id );
00455     if( idType == GetDiscoInfo )
00456       iq.addExtension( new Info( node ) );
00457     else
00458       iq.addExtension( new Items( node ) );
00459 
00460     DiscoHandlerContext ct;
00461     ct.dh = dh;
00462     ct.context = context;
00463     m_track[id] = ct;
00464     m_parent->send( iq, this, idType );
00465   }
00466 
00467   void Disco::setVersion( const std::string& name, const std::string& version, const std::string& os )
00468   {
00469     m_versionName = name;
00470     m_versionVersion = version;
00471     m_versionOs = os;
00472   }
00473 
00474   void Disco::setIdentity( const std::string& category, const std::string& type,
00475                            const std::string& name )
00476   {
00477     util::clearList( m_identities );
00478     addIdentity( category, type, name );
00479   }
00480 
00481   void Disco::removeDiscoHandler( DiscoHandler* dh )
00482   {
00483     m_discoHandlers.remove( dh );
00484     DiscoHandlerMap::iterator t;
00485     DiscoHandlerMap::iterator it = m_track.begin();
00486     while( it != m_track.end() )
00487     {
00488       t = it;
00489       ++it;
00490       if( dh == (*t).second.dh )
00491       {
00492         m_track.erase( t );
00493       }
00494     }
00495   }
00496 
00497   void Disco::registerNodeHandler( DiscoNodeHandler* nh, const std::string& node )
00498   {
00499     m_nodeHandlers[node].push_back( nh );
00500   }
00501 
00502   void Disco::removeNodeHandler( DiscoNodeHandler* nh, const std::string& node )
00503   {
00504     DiscoNodeHandlerMap::iterator it = m_nodeHandlers.find( node );
00505     if( it != m_nodeHandlers.end() )
00506     {
00507       (*it).second.remove( nh );
00508       if( (*it).second.empty() )
00509         m_nodeHandlers.erase( it );
00510     }
00511   }
00512 
00513   void Disco::removeNodeHandlers( DiscoNodeHandler* nh )
00514   {
00515     DiscoNodeHandlerMap::iterator it = m_nodeHandlers.begin();
00516     DiscoNodeHandlerMap::iterator it2;
00517     while( it != m_nodeHandlers.end() )
00518     {
00519       it2 = it++;
00520       removeNodeHandler( nh, (*it2).first );
00521     }
00522   }
00523 
00524   const StringList Disco::features( bool defaultFeatures ) const
00525   {
00526     StringList f = m_features;
00527     if( defaultFeatures )
00528     {
00529       f.push_back( XMLNS_DISCO_INFO );
00530       f.push_back( XMLNS_DISCO_ITEMS );
00531     }
00532     return f;
00533   }
00534 
00535 }