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 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_form( 0 ), m_action( action ),
00082 m_status( InvalidStatus ), m_actions( 0 )
00083 {
00084 }
00085
00086 Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status,
00087 DataForm* form )
00088 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
00089 m_form( form ), m_action( InvalidAction ), m_status( status ), m_actions( 0 )
00090 {
00091 }
00092
00093 Adhoc::Command::Command( const std::string& node, const std::string& sessionid,
00094 Adhoc::Command::Action action )
00095 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
00096 m_form( 0 ), m_action( action ), m_actions( 0 )
00097 {
00098 }
00099
00100 Adhoc::Command::Command( const Tag* tag )
00101 : StanzaExtension( ExtAdhocCommand ), m_form( 0 ), m_actions( 0 )
00102 {
00103 if( !tag || tag->name() != "command" || tag->xmlns() != XMLNS_ADHOC_COMMANDS )
00104 return;
00105
00106 m_node = tag->findAttribute( "node" );
00107 m_sessionid = tag->findAttribute( "sessionid" );
00108 m_action = (Action)util::deflookup2( tag->findAttribute( "action" ), cmdActionStringValues, Execute );
00109 m_status = (Status)util::lookup( tag->findAttribute( "status" ), cmdStatusStringValues );
00110
00111 Tag* a = tag->findChild( "actions" );
00112 if( a )
00113 {
00114 if( a->hasChild( "prev" ) )
00115 m_actions |= Previous;
00116 if( a->hasChild( "next" ) )
00117 m_actions |= Next;
00118 if( a->hasChild( "complete" ) )
00119 m_actions |= Complete;
00120 }
00121
00122 const ConstTagList& l = tag->findTagList( "/command/note" );
00123 ConstTagList::const_iterator it = l.begin();
00124 for( ; it != l.end(); ++it )
00125 m_notes.push_back( new Note( (*it) ) );
00126
00127 Tag* x = tag->findChild( "x", "xmlns", XMLNS_X_DATA );
00128 if( x )
00129 m_form = new DataForm( x );
00130 }
00131
00132 Adhoc::Command::~Command()
00133 {
00134 util::clearList( m_notes );
00135 delete m_form;
00136 }
00137
00138 const std::string& Adhoc::Command::filterString() const
00139 {
00140 static const std::string filter = "/iq/command[@xmlns='" + XMLNS_ADHOC_COMMANDS + "']";
00141 return filter;
00142 }
00143
00144 Tag* Adhoc::Command::tag() const
00145 {
00146 if( m_node.empty() )
00147 return 0;
00148
00149 Tag* c = new Tag( "command" );
00150 c->setXmlns( XMLNS_ADHOC_COMMANDS );
00151 c->addAttribute( "node", m_node );
00152 if( m_action != InvalidAction )
00153 c->addAttribute( "action", actionString( m_action ) );
00154 if( m_status != InvalidStatus )
00155 c->addAttribute( "status", statusString( m_status ) );
00156 if ( !m_sessionid.empty() )
00157 c->addAttribute( "sessionid", m_sessionid );
00158
00159 if( m_form && *m_form )
00160 c->addChild( m_form->tag() );
00161
00162 NoteList::const_iterator it = m_notes.begin();
00163 for( ; it != m_notes.end(); ++it )
00164 c->addChild( (*it)->tag() );
00165
00166 return c;
00167 }
00168
00169
00170
00171 Adhoc::Adhoc( ClientBase* parent )
00172 : m_parent( parent )
00173 {
00174 if( !m_parent || !m_parent->disco() )
00175 return;
00176
00177 m_parent->disco()->addFeature( XMLNS_ADHOC_COMMANDS );
00178 m_parent->disco()->registerNodeHandler( this, XMLNS_ADHOC_COMMANDS );
00179 m_parent->disco()->registerNodeHandler( this, EmptyString );
00180 m_parent->registerIqHandler( this, ExtAdhocCommand );
00181 m_parent->registerStanzaExtension( new Adhoc::Command() );
00182 }
00183
00184 Adhoc::~Adhoc()
00185 {
00186 if( !m_parent || !m_parent->disco() )
00187 return;
00188
00189 m_parent->disco()->removeFeature( XMLNS_ADHOC_COMMANDS );
00190 m_parent->disco()->removeNodeHandler( this, XMLNS_ADHOC_COMMANDS );
00191 m_parent->disco()->removeNodeHandler( this, EmptyString );
00192 m_parent->removeIqHandler( this, ExtAdhocCommand );
00193 m_parent->removeIDHandler( this );
00194 m_parent->removeStanzaExtension( ExtAdhocCommand );
00195 }
00196
00197 StringList Adhoc::handleDiscoNodeFeatures( const JID& , const std::string& )
00198 {
00199 StringList features;
00200 features.push_back( XMLNS_ADHOC_COMMANDS );
00201 return features;
00202
00203 }
00204
00205 Disco::ItemList Adhoc::handleDiscoNodeItems( const JID& , const std::string& node )
00206 {
00207 Disco::ItemList l;
00208 if( node.empty() )
00209 {
00210 l.push_back( new Disco::Item( m_parent->jid(), XMLNS_ADHOC_COMMANDS, "Ad-Hoc Commands" ) );
00211 }
00212 else if( node == XMLNS_ADHOC_COMMANDS )
00213 {
00214 StringMap::const_iterator it = m_items.begin();
00215 for( ; it != m_items.end(); ++it )
00216 {
00217 l.push_back( new Disco::Item( m_parent->jid(), (*it).first, (*it).second ) );
00218 }
00219 }
00220 return l;
00221 }
00222
00223 Disco::IdentityList Adhoc::handleDiscoNodeIdentities( const JID& , const std::string& node )
00224 {
00225 Disco::IdentityList l;
00226 StringMap::const_iterator it = m_items.find( node );
00227 l.push_back( new Disco::Identity( "automation",
00228 node == XMLNS_ADHOC_COMMANDS ? "command-list" : "command-node",
00229 it == m_items.end() ? "Ad-Hoc Commands" : (*it).second ) );
00230 return l;
00231 }
00232
00233 bool Adhoc::handleIq( const IQ& iq )
00234 {
00235 if( iq.subtype() != IQ::Set )
00236 return false;
00237
00238 const Adhoc::Command* ac = iq.findExtension<Adhoc::Command>( ExtAdhocCommand );
00239 if( !ac || ac->node().empty())
00240 return false;
00241
00242 AdhocCommandProviderMap::const_iterator it = m_adhocCommandProviders.find( ac->node() );
00243 if( it != m_adhocCommandProviders.end() )
00244 {
00245 const std::string& sess = ac->sessionID().empty() ? m_parent->getID() : ac->sessionID();
00246 m_activeSessions[sess] = iq.id();
00247 (*it).second->handleAdhocCommand( iq.from(), *ac, sess );
00248 return true;
00249 }
00250
00251 return false;
00252 }
00253
00254 void Adhoc::handleIqID( const IQ& iq, int context )
00255 {
00256 if( context != ExecuteAdhocCommand )
00257 return;
00258
00259 AdhocTrackMap::iterator it = m_adhocTrackMap.find( iq.id() );
00260 if( it == m_adhocTrackMap.end() || (*it).second.context != context
00261 || (*it).second.remote != iq.from() )
00262 return;
00263
00264 switch( iq.subtype() )
00265 {
00266 case IQ::Error:
00267 (*it).second.ah->handleAdhocError( iq.from(), iq.error() );
00268 break;
00269 case IQ::Result:
00270 {
00271 const Adhoc::Command* ac = iq.findExtension<Adhoc::Command>( ExtAdhocCommand );
00272 if( ac )
00273 (*it).second.ah->handleAdhocExecutionResult( iq.from(), *ac );
00274 break;
00275 }
00276 default:
00277 break;
00278 }
00279 m_adhocTrackMap.erase( it );
00280 }
00281
00282 void Adhoc::registerAdhocCommandProvider( AdhocCommandProvider* acp, const std::string& command,
00283 const std::string& name )
00284 {
00285 if( !m_parent || !m_parent->disco() )
00286 return;
00287
00288 m_parent->disco()->registerNodeHandler( this, command );
00289 m_adhocCommandProviders[command] = acp;
00290 m_items[command] = name;
00291 }
00292
00293 void Adhoc::handleDiscoInfo( const JID& from, const Disco::Info& info, int context )
00294 {
00295 if( context != CheckAdhocSupport )
00296 return;
00297
00298 AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
00299 for( ; it != m_adhocTrackMap.end() && (*it).second.context != context
00300 && (*it).second.remote != from; ++it )
00301 ;
00302 if( it == m_adhocTrackMap.end() )
00303 return;
00304
00305 (*it).second.ah->handleAdhocSupport( from, info.hasFeature( XMLNS_ADHOC_COMMANDS ) );
00306 m_adhocTrackMap.erase( it );
00307 }
00308
00309 void Adhoc::handleDiscoItems( const JID& from, const Disco::Items& items, int context )
00310 {
00311 if( context != FetchAdhocCommands )
00312 return;
00313
00314 AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
00315 for( ; it != m_adhocTrackMap.end(); ++it )
00316 {
00317 if( (*it).second.context == context && (*it).second.remote == from )
00318 {
00319 StringMap commands;
00320 const Disco::ItemList& l = items.items();
00321 Disco::ItemList::const_iterator it2 = l.begin();
00322 for( ; it2 != l.end(); ++it2 )
00323 {
00324 commands[(*it2)->node()] = (*it2)->name();
00325 }
00326 (*it).second.ah->handleAdhocCommands( from, commands );
00327
00328 m_adhocTrackMap.erase( it );
00329 break;
00330 }
00331 }
00332 }
00333
00334 void Adhoc::handleDiscoError( const JID& from, const Error* error, int context )
00335 {
00336 AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
00337 for( ; it != m_adhocTrackMap.end(); ++it )
00338 {
00339 if( (*it).second.context == context && (*it).second.remote == from )
00340 {
00341 (*it).second.ah->handleAdhocError( from, error );
00342
00343 m_adhocTrackMap.erase( it );
00344 }
00345 }
00346 }
00347
00348 void Adhoc::checkSupport( const JID& remote, AdhocHandler* ah )
00349 {
00350 if( !remote || !ah || !m_parent || !m_parent->disco() )
00351 return;
00352
00353 TrackStruct track;
00354 track.remote = remote;
00355 track.context = CheckAdhocSupport;
00356 track.ah = ah;
00357 const std::string& id = m_parent->getID();
00358 m_adhocTrackMap[id] = track;
00359 m_parent->disco()->getDiscoInfo( remote, EmptyString, this, CheckAdhocSupport, id );
00360 }
00361
00362 void Adhoc::getCommands( const JID& remote, AdhocHandler* ah )
00363 {
00364 if( !remote || !ah || !m_parent || !m_parent->disco() )
00365 return;
00366
00367 TrackStruct track;
00368 track.remote = remote;
00369 track.context = FetchAdhocCommands;
00370 track.ah = ah;
00371 const std::string& id = m_parent->getID();
00372 m_adhocTrackMap[id] = track;
00373 m_parent->disco()->getDiscoItems( remote, XMLNS_ADHOC_COMMANDS, this, FetchAdhocCommands, id );
00374 }
00375
00376 void Adhoc::execute( const JID& remote, const Adhoc::Command* command, AdhocHandler* ah )
00377 {
00378 if( !remote || !command || !m_parent || !ah )
00379 return;
00380
00381 const std::string& id = m_parent->getID();
00382 IQ iq( IQ::Set, remote, id );
00383 iq.addExtension( command );
00384
00385 TrackStruct track;
00386 track.remote = remote;
00387 track.context = ExecuteAdhocCommand;
00388 track.session = command->sessionID();
00389 track.ah = ah;
00390 m_adhocTrackMap[id] = track;
00391
00392 m_parent->send( iq, this, ExecuteAdhocCommand );
00393 }
00394
00395 void Adhoc::respond( const JID& remote, const Adhoc::Command* command, const Error* error )
00396 {
00397 if( !remote || !command || !m_parent )
00398 return;
00399
00400 StringMap::iterator it = m_activeSessions.find( command->sessionID() );
00401 if( it == m_activeSessions.end() )
00402 return;
00403
00404 IQ re( error ? IQ::Error : IQ::Result, remote, (*it).second );
00405 re.addExtension( command );
00406 if( error )
00407 re.addExtension( error );
00408 m_parent->send( re );
00409 m_activeSessions.erase( it );
00410 }
00411
00412 void Adhoc::removeAdhocCommandProvider( const std::string& command )
00413 {
00414 if( !m_parent || !m_parent->disco() )
00415 return;
00416
00417 m_parent->disco()->removeNodeHandler( this, command );
00418 m_adhocCommandProviders.erase( command );
00419 m_items.erase( command );
00420 }
00421
00422 }