gloox
1.0
|
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 "adhoc.h" 00015 #include "adhochandler.h" 00016 #include "adhoccommandprovider.h" 00017 #include "disco.h" 00018 #include "error.h" 00019 #include "discohandler.h" 00020 #include "clientbase.h" 00021 #include "dataform.h" 00022 #include "util.h" 00023 00024 namespace gloox 00025 { 00026 00027 static const char* cmdActionStringValues[] = 00028 { 00029 "execute", "cancel", "prev", "next", "complete" 00030 }; 00031 00032 static inline const std::string actionString( Adhoc::Command::Action action ) 00033 { 00034 return util::lookup2( action, cmdActionStringValues ); 00035 } 00036 00037 static const char* cmdStatusStringValues[] = 00038 { 00039 "executing", "completed", "canceled" 00040 }; 00041 00042 static inline const std::string statusString( Adhoc::Command::Status status ) 00043 { 00044 return util::lookup( status, cmdStatusStringValues ); 00045 } 00046 00047 static const char* cmdNoteStringValues[] = 00048 { 00049 "info", "warn", "error" 00050 }; 00051 00052 static inline const std::string noteString( Adhoc::Command::Note::Severity sev ) 00053 { 00054 return util::lookup( sev, cmdNoteStringValues ); 00055 } 00056 00057 // ---- Adhoc::Command::Note ---- 00058 Adhoc::Command::Note::Note( const Tag* tag ) 00059 : m_severity( InvalidSeverity ) 00060 { 00061 if( !tag || tag->name() != "note" ) 00062 return; 00063 00064 m_severity = (Severity)util::deflookup( tag->findAttribute( "type" ), cmdNoteStringValues, Info ); 00065 m_note = tag->cdata(); 00066 } 00067 00068 Tag* Adhoc::Command::Note::tag() const 00069 { 00070 if( m_note.empty() || m_severity == InvalidSeverity ) 00071 return 0; 00072 00073 Tag* n = new Tag( "note", m_note ); 00074 n->addAttribute( TYPE, noteString( m_severity ) ); 00075 return n; 00076 } 00077 // ---- ~Adhoc::Command::Note ---- 00078 00079 // ---- Adhoc::Command ---- 00080 Adhoc::Command::Command( const std::string& node, Adhoc::Command::Action action, 00081 DataForm* form ) 00082 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_form( form ), m_action( action ), 00083 m_status( InvalidStatus ), m_actions( 0 ) 00084 { 00085 } 00086 00087 Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status, 00088 DataForm* form ) 00089 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ), 00090 m_form( form ), m_action( InvalidAction ), m_status( status ), m_actions( 0 ) 00091 { 00092 } 00093 00094 Adhoc::Command::Command( const std::string& node, const std::string& sessionid, 00095 Adhoc::Command::Action action, 00096 DataForm* form ) 00097 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ), 00098 m_form( form ), m_action( action ), m_actions( 0 ) 00099 { 00100 } 00101 00102 Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status, 00103 Action executeAction, int allowedActions, 00104 DataForm* form ) 00105 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ), 00106 m_form( form ), m_action( executeAction ), m_status( status ), m_actions( allowedActions ) 00107 { 00108 } 00109 00110 Adhoc::Command::Command( const Tag* tag ) 00111 : StanzaExtension( ExtAdhocCommand ), m_form( 0 ), m_actions( 0 ) 00112 { 00113 if( !tag || tag->name() != "command" || tag->xmlns() != XMLNS_ADHOC_COMMANDS ) 00114 return; 00115 00116 m_node = tag->findAttribute( "node" ); 00117 m_sessionid = tag->findAttribute( "sessionid" ); 00118 m_status = (Status)util::lookup( tag->findAttribute( "status" ), cmdStatusStringValues ); 00119 00120 Tag* a = tag->findChild( "actions" ); 00121 if( a ) 00122 { 00123 // Multi-stage response 00124 m_action = (Action)util::deflookup2( a->findAttribute( "action" ), cmdActionStringValues, Complete ); 00125 if( a->hasChild( "prev" ) ) 00126 m_actions |= Previous; 00127 if( a->hasChild( "next" ) ) 00128 m_actions |= Next; 00129 if( a->hasChild( "complete" ) ) 00130 m_actions |= Complete; 00131 } 00132 else 00133 { 00134 m_action = (Action)util::deflookup2( tag->findAttribute( "action" ), cmdActionStringValues, Execute ); 00135 } 00136 00137 const ConstTagList& l = tag->findTagList( "/command/note" ); 00138 ConstTagList::const_iterator it = l.begin(); 00139 for( ; it != l.end(); ++it ) 00140 m_notes.push_back( new Note( (*it) ) ); 00141 00142 Tag* x = tag->findChild( "x", "xmlns", XMLNS_X_DATA ); 00143 if( x ) 00144 m_form = new DataForm( x ); 00145 } 00146 00147 Adhoc::Command::~Command() 00148 { 00149 util::clearList( m_notes ); 00150 delete m_form; 00151 } 00152 00153 const std::string& Adhoc::Command::filterString() const 00154 { 00155 static const std::string filter = "/iq/command[@xmlns='" + XMLNS_ADHOC_COMMANDS + "']"; 00156 return filter; 00157 } 00158 00159 Tag* Adhoc::Command::tag() const 00160 { 00161 if( m_node.empty() ) 00162 return 0; 00163 00164 Tag* c = new Tag( "command" ); 00165 c->setXmlns( XMLNS_ADHOC_COMMANDS ); 00166 c->addAttribute( "node", m_node ); 00167 if( m_actions != 0 ) 00168 { 00169 // Multi-stage command response 00170 00171 if( m_status != InvalidStatus ) 00172 c->addAttribute( "status", statusString( m_status ) ); 00173 else 00174 c->addAttribute( "status", statusString( Executing ) ); 00175 00176 Tag* actions = new Tag( c, "actions" ); 00177 00178 if( m_action != InvalidAction ) 00179 c->addAttribute( "execute", actionString( m_action ) ); 00180 else 00181 c->addAttribute( "execute", actionString( Complete ) ); 00182 00183 if( ( m_actions & Previous ) == Previous ) 00184 new Tag( actions, "prev" ); 00185 if( ( m_actions & Next ) == Next ) 00186 new Tag( actions, "next" ); 00187 if( ( m_actions & Complete ) == Complete ) 00188 new Tag( actions, "complete" ); 00189 } 00190 else 00191 { 00192 // Single-stage command request/response or Multi-stage command request 00193 00194 if( m_action != InvalidAction ) 00195 c->addAttribute( "action", actionString( m_action ) ); 00196 if( m_status != InvalidStatus ) 00197 c->addAttribute( "status", statusString( m_status ) ); 00198 } 00199 00200 if ( !m_sessionid.empty() ) 00201 c->addAttribute( "sessionid", m_sessionid ); 00202 00203 if( m_form && *m_form ) 00204 c->addChild( m_form->tag() ); 00205 00206 NoteList::const_iterator it = m_notes.begin(); 00207 for( ; it != m_notes.end(); ++it ) 00208 c->addChild( (*it)->tag() ); 00209 00210 return c; 00211 } 00212 // ---- ~Adhoc::Command ---- 00213 00214 // ---- Adhoc ---- 00215 Adhoc::Adhoc( ClientBase* parent ) 00216 : m_parent( parent ) 00217 { 00218 if( !m_parent || !m_parent->disco() ) 00219 return; 00220 00221 m_parent->disco()->addFeature( XMLNS_ADHOC_COMMANDS ); 00222 m_parent->disco()->registerNodeHandler( this, XMLNS_ADHOC_COMMANDS ); 00223 m_parent->disco()->registerNodeHandler( this, EmptyString ); 00224 m_parent->registerIqHandler( this, ExtAdhocCommand ); 00225 m_parent->registerStanzaExtension( new Adhoc::Command() ); 00226 } 00227 00228 Adhoc::~Adhoc() 00229 { 00230 if( !m_parent || !m_parent->disco() ) 00231 return; 00232 00233 m_parent->disco()->removeFeature( XMLNS_ADHOC_COMMANDS ); 00234 m_parent->disco()->removeNodeHandler( this, XMLNS_ADHOC_COMMANDS ); 00235 m_parent->disco()->removeNodeHandler( this, EmptyString ); 00236 m_parent->removeIqHandler( this, ExtAdhocCommand ); 00237 m_parent->removeIDHandler( this ); 00238 m_parent->removeStanzaExtension( ExtAdhocCommand ); 00239 } 00240 00241 StringList Adhoc::handleDiscoNodeFeatures( const JID& /*from*/, const std::string& /*node*/ ) 00242 { 00243 StringList features; 00244 features.push_back( XMLNS_ADHOC_COMMANDS ); 00245 return features; 00246 // return StringList( 1, XMLNS_ADHOC_COMMANDS ); 00247 } 00248 00249 Disco::ItemList Adhoc::handleDiscoNodeItems( const JID& from, const JID& /*to*/, const std::string& node ) 00250 { 00251 Disco::ItemList l; 00252 if( node.empty() ) 00253 { 00254 l.push_back( new Disco::Item( m_parent->jid(), XMLNS_ADHOC_COMMANDS, "Ad-Hoc Commands" ) ); 00255 } 00256 else if( node == XMLNS_ADHOC_COMMANDS ) 00257 { 00258 StringMap::const_iterator it = m_items.begin(); 00259 for( ; it != m_items.end(); ++it ) 00260 { 00261 AdhocCommandProviderMap::const_iterator itp = m_adhocCommandProviders.find( (*it).first ); 00262 if( itp != m_adhocCommandProviders.end() 00263 && (*itp).second 00264 && (*itp).second->handleAdhocAccessRequest( from, (*it).first ) ) 00265 { 00266 l.push_back( new Disco::Item( m_parent->jid(), (*it).first, (*it).second ) ); 00267 } 00268 } 00269 } 00270 return l; 00271 } 00272 00273 Disco::IdentityList Adhoc::handleDiscoNodeIdentities( const JID& /*from*/, const std::string& node ) 00274 { 00275 Disco::IdentityList l; 00276 StringMap::const_iterator it = m_items.find( node ); 00277 l.push_back( new Disco::Identity( "automation", 00278 node == XMLNS_ADHOC_COMMANDS ? "command-list" : "command-node", 00279 it == m_items.end() ? "Ad-Hoc Commands" : (*it).second ) ); 00280 return l; 00281 } 00282 00283 bool Adhoc::handleIq( const IQ& iq ) 00284 { 00285 if( iq.subtype() != IQ::Set ) 00286 return false; 00287 00288 const Adhoc::Command* ac = iq.findExtension<Adhoc::Command>( ExtAdhocCommand ); 00289 if( !ac || ac->node().empty()) 00290 return false; 00291 00292 AdhocCommandProviderMap::const_iterator it = m_adhocCommandProviders.find( ac->node() ); 00293 if( it != m_adhocCommandProviders.end() ) 00294 { 00295 const std::string& sess = ac->sessionID().empty() ? m_parent->getID() : ac->sessionID(); 00296 m_activeSessions[sess] = iq.id(); 00297 (*it).second->handleAdhocCommand( iq.from(), *ac, sess ); 00298 return true; 00299 } 00300 00301 return false; 00302 } 00303 00304 void Adhoc::handleIqID( const IQ& iq, int context ) 00305 { 00306 if( context != ExecuteAdhocCommand ) 00307 return; 00308 00309 AdhocTrackMap::iterator it = m_adhocTrackMap.find( iq.id() ); 00310 if( it == m_adhocTrackMap.end() || (*it).second.context != context 00311 || (*it).second.remote != iq.from() ) 00312 return; 00313 00314 switch( iq.subtype() ) 00315 { 00316 case IQ::Error: 00317 (*it).second.ah->handleAdhocError( iq.from(), iq.error() ); 00318 break; 00319 case IQ::Result: 00320 { 00321 const Adhoc::Command* ac = iq.findExtension<Adhoc::Command>( ExtAdhocCommand ); 00322 if( ac ) 00323 (*it).second.ah->handleAdhocExecutionResult( iq.from(), *ac ); 00324 break; 00325 } 00326 default: 00327 break; 00328 } 00329 m_adhocTrackMap.erase( it ); 00330 } 00331 00332 void Adhoc::registerAdhocCommandProvider( AdhocCommandProvider* acp, const std::string& command, 00333 const std::string& name ) 00334 { 00335 if( !m_parent || !m_parent->disco() ) 00336 return; 00337 00338 m_parent->disco()->registerNodeHandler( this, command ); 00339 m_adhocCommandProviders[command] = acp; 00340 m_items[command] = name; 00341 } 00342 00343 void Adhoc::handleDiscoInfo( const JID& from, const Disco::Info& info, int context ) 00344 { 00345 if( context != CheckAdhocSupport ) 00346 return; 00347 00348 AdhocTrackMap::iterator it = m_adhocTrackMap.begin(); 00349 for( ; it != m_adhocTrackMap.end() && (*it).second.context != context 00350 && (*it).second.remote != from; ++it ) 00351 ; 00352 if( it == m_adhocTrackMap.end() ) 00353 return; 00354 00355 (*it).second.ah->handleAdhocSupport( from, info.hasFeature( XMLNS_ADHOC_COMMANDS ) ); 00356 m_adhocTrackMap.erase( it ); 00357 } 00358 00359 void Adhoc::handleDiscoItems( const JID& from, const Disco::Items& items, int context ) 00360 { 00361 if( context != FetchAdhocCommands ) 00362 return; 00363 00364 AdhocTrackMap::iterator it = m_adhocTrackMap.begin(); 00365 for( ; it != m_adhocTrackMap.end(); ++it ) 00366 { 00367 if( (*it).second.context == context && (*it).second.remote == from ) 00368 { 00369 StringMap commands; 00370 const Disco::ItemList& l = items.items(); 00371 Disco::ItemList::const_iterator it2 = l.begin(); 00372 for( ; it2 != l.end(); ++it2 ) 00373 { 00374 commands[(*it2)->node()] = (*it2)->name(); 00375 } 00376 (*it).second.ah->handleAdhocCommands( from, commands ); 00377 00378 m_adhocTrackMap.erase( it ); 00379 break; 00380 } 00381 } 00382 } 00383 00384 void Adhoc::handleDiscoError( const JID& from, const Error* error, int context ) 00385 { 00386 AdhocTrackMap::iterator it = m_adhocTrackMap.begin(); 00387 for( ; it != m_adhocTrackMap.end(); ++it ) 00388 { 00389 if( (*it).second.context == context && (*it).second.remote == from ) 00390 { 00391 (*it).second.ah->handleAdhocError( from, error ); 00392 00393 m_adhocTrackMap.erase( it ); 00394 } 00395 } 00396 } 00397 00398 void Adhoc::checkSupport( const JID& remote, AdhocHandler* ah ) 00399 { 00400 if( !remote || !ah || !m_parent || !m_parent->disco() ) 00401 return; 00402 00403 TrackStruct track; 00404 track.remote = remote; 00405 track.context = CheckAdhocSupport; 00406 track.ah = ah; 00407 const std::string& id = m_parent->getID(); 00408 m_adhocTrackMap[id] = track; 00409 m_parent->disco()->getDiscoInfo( remote, EmptyString, this, CheckAdhocSupport, id ); 00410 } 00411 00412 void Adhoc::getCommands( const JID& remote, AdhocHandler* ah ) 00413 { 00414 if( !remote || !ah || !m_parent || !m_parent->disco() ) 00415 return; 00416 00417 TrackStruct track; 00418 track.remote = remote; 00419 track.context = FetchAdhocCommands; 00420 track.ah = ah; 00421 const std::string& id = m_parent->getID(); 00422 m_adhocTrackMap[id] = track; 00423 m_parent->disco()->getDiscoItems( remote, XMLNS_ADHOC_COMMANDS, this, FetchAdhocCommands, id ); 00424 } 00425 00426 void Adhoc::execute( const JID& remote, const Adhoc::Command* command, AdhocHandler* ah ) 00427 { 00428 if( !remote || !command || !m_parent || !ah ) 00429 return; 00430 00431 const std::string& id = m_parent->getID(); 00432 IQ iq( IQ::Set, remote, id ); 00433 iq.addExtension( command ); 00434 00435 TrackStruct track; 00436 track.remote = remote; 00437 track.context = ExecuteAdhocCommand; 00438 track.session = command->sessionID(); 00439 track.ah = ah; 00440 m_adhocTrackMap[id] = track; 00441 00442 m_parent->send( iq, this, ExecuteAdhocCommand ); 00443 } 00444 00445 void Adhoc::respond( const JID& remote, const Adhoc::Command* command, const Error* error ) 00446 { 00447 if( !remote || !command || !m_parent ) 00448 return; 00449 00450 StringMap::iterator it = m_activeSessions.find( command->sessionID() ); 00451 if( it == m_activeSessions.end() ) 00452 return; 00453 00454 IQ re( error ? IQ::Error : IQ::Result, remote, (*it).second ); 00455 re.addExtension( command ); 00456 if( error ) 00457 re.addExtension( error ); 00458 m_parent->send( re ); 00459 m_activeSessions.erase( it ); 00460 } 00461 00462 void Adhoc::removeAdhocCommandProvider( const std::string& command ) 00463 { 00464 if( !m_parent || !m_parent->disco() ) 00465 return; 00466 00467 m_parent->disco()->removeNodeHandler( this, command ); 00468 m_adhocCommandProviders.erase( command ); 00469 m_items.erase( command ); 00470 } 00471 00472 }