00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
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
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
00078
00079
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
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
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
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
00213
00214
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& , const std::string& )
00242 {
00243 StringList features;
00244 features.push_back( XMLNS_ADHOC_COMMANDS );
00245 return features;
00246
00247 }
00248
00249 Disco::ItemList Adhoc::handleDiscoNodeItems( const JID& from, const JID& , 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& , 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 }