gloox 1.0.28
adhoc.cpp
1/*
2 Copyright (c) 2004-2023 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
14#include "adhoc.h"
15#include "adhochandler.h"
16#include "adhoccommandprovider.h"
17#include "disco.h"
18#include "dataform.h"
19#include "error.h"
20#include "iodata.h"
21#include "discohandler.h"
22#include "clientbase.h"
23#include "adhocplugin.h"
24#include "util.h"
25#include "mutexguard.h"
26
27namespace gloox
28{
29
30 static const char* cmdActionStringValues[] =
31 {
32 "execute", "cancel", "prev", "next", "complete"
33 };
34
35 static inline const std::string actionString( Adhoc::Command::Action action )
36 {
37 return util::lookup2( action, cmdActionStringValues );
38 }
39
40 static const char* cmdStatusStringValues[] =
41 {
42 "executing", "completed", "canceled"
43 };
44
45 static inline const std::string statusString( Adhoc::Command::Status status )
46 {
47 return util::lookup( status, cmdStatusStringValues );
48 }
49
50 static const char* cmdNoteStringValues[] =
51 {
52 "info", "warn", "error"
53 };
54
55 static inline const std::string noteString( Adhoc::Command::Note::Severity sev )
56 {
57 return util::lookup( sev, cmdNoteStringValues );
58 }
59
60 // ---- Adhoc::Command::Note ----
62 : m_severity( InvalidSeverity )
63 {
64 if( !tag || tag->name() != "note" )
65 return;
66
67 m_severity = static_cast<Severity>( util::deflookup( tag->findAttribute( "type" ), cmdNoteStringValues, Info ) );
68 m_note = tag->cdata();
69 }
70
72 {
73 if( m_note.empty() || m_severity == InvalidSeverity )
74 return 0;
75
76 Tag* n = new Tag( "note", m_note );
77 n->addAttribute( TYPE, noteString( m_severity ) );
78 return n;
79 }
80 // ---- ~Adhoc::Command::Note ----
81
82 // ---- Adhoc::Command ----
85 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_plugin( plugin ), m_action( action ),
86 m_status( InvalidStatus ), m_actions( 0 )
87 {
88 }
89
90 Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status,
91 AdhocPlugin* plugin )
92 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
93 m_plugin( plugin ), m_action( InvalidAction ), m_status( status ), m_actions( 0 )
94 {
95 }
96
97 Adhoc::Command::Command( const std::string& node, const std::string& sessionid,
99 AdhocPlugin* plugin )
100 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
101 m_plugin( plugin ), m_action( action ), m_actions( 0 )
102 {
103 }
104
105 Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status,
106 Action executeAction, int allowedActions,
107 AdhocPlugin* plugin )
108 : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
109 m_plugin( plugin ), m_action( executeAction ), m_status( status ), m_actions( allowedActions )
110 {
111 }
112
114 : StanzaExtension( ExtAdhocCommand ), m_plugin( 0 ), m_actions( 0 )
115 {
116 if( !tag || tag->name() != "command" || tag->xmlns() != XMLNS_ADHOC_COMMANDS )
117 return;
118
119 m_node = tag->findAttribute( "node" );
120 m_sessionid = tag->findAttribute( "sessionid" );
121 m_status = static_cast<Status>( util::lookup( tag->findAttribute( "status" ), cmdStatusStringValues ) );
122
123 Tag* a = tag->findChild( "actions" );
124 if( a )
125 {
126 // Multi-stage response
127 m_action = static_cast<Action>( util::deflookup2( a->findAttribute( "action" ), cmdActionStringValues, Complete ) );
128 if( a->hasChild( "prev" ) )
129 m_actions |= Previous;
130 if( a->hasChild( "next" ) )
131 m_actions |= Next;
132 if( a->hasChild( "complete" ) )
133 m_actions |= Complete;
134 }
135 else
136 {
137 m_action = static_cast<Action>( util::deflookup2( tag->findAttribute( "action" ), cmdActionStringValues, Execute ) );
138 }
139
140 const ConstTagList& l = tag->findTagList( "/command/note" );
141 ConstTagList::const_iterator it = l.begin();
142 for( ; it != l.end(); ++it )
143 m_notes.push_back( new Note( (*it) ) );
144
145 Tag* x = tag->findChild( "x", "xmlns", XMLNS_X_DATA );
146 if( x )
147 m_plugin = new DataForm( x );
148 else
149 {
150 Tag* x = tag->findChild( "iodata", "xmlns", XMLNS_IODATA );
151 if( x )
152 m_plugin = new IOData( x );
153 }
154 }
155
157 {
158 util::clearList( m_notes );
159 delete m_plugin;
160 }
161
162 const std::string& Adhoc::Command::filterString() const
163 {
164 static const std::string filter = "/iq/command[@xmlns='" + XMLNS_ADHOC_COMMANDS + "']";
165 return filter;
166 }
167
169 {
170 if( m_node.empty() )
171 return 0;
172
173 Tag* c = new Tag( "command" );
175 c->addAttribute( "node", m_node );
176 if( m_actions != 0 )
177 {
178 // Multi-stage command response
179
180 if( m_status != InvalidStatus )
181 c->addAttribute( "status", statusString( m_status ) );
182 else
183 c->addAttribute( "status", statusString( Executing ) );
184
185 Tag* actions = new Tag( c, "actions" );
186
187 if( m_action != InvalidAction )
188 c->addAttribute( "execute", actionString( m_action ) );
189 else
190 c->addAttribute( "execute", actionString( Complete ) );
191
192 if( ( m_actions & Previous ) == Previous )
193 new Tag( actions, "prev" );
194 if( ( m_actions & Next ) == Next )
195 new Tag( actions, "next" );
196 if( ( m_actions & Complete ) == Complete )
197 new Tag( actions, "complete" );
198 }
199 else
200 {
201 // Single-stage command request/response or Multi-stage command request
202
203 if( m_action != InvalidAction )
204 c->addAttribute( "action", actionString( m_action ) );
205 if( m_status != InvalidStatus )
206 c->addAttribute( "status", statusString( m_status ) );
207 }
208
209 if ( !m_sessionid.empty() )
210 c->addAttribute( "sessionid", m_sessionid );
211
212 if( m_plugin && *m_plugin )
213 c->addChild( m_plugin->tag() );
214
215 NoteList::const_iterator it = m_notes.begin();
216 for( ; it != m_notes.end(); ++it )
217 c->addChild( (*it)->tag() );
218
219 return c;
220 }
221 // ---- ~Adhoc::Command ----
222
223 // ---- Adhoc ----
225 : m_parent( parent )
226 {
227 if( !m_parent || !m_parent->disco() )
228 return;
229
230 m_parent->disco()->addFeature( XMLNS_ADHOC_COMMANDS );
231 m_parent->disco()->registerNodeHandler( this, XMLNS_ADHOC_COMMANDS );
232 m_parent->disco()->registerNodeHandler( this, EmptyString );
233 m_parent->registerIqHandler( this, ExtAdhocCommand );
234 m_parent->registerStanzaExtension( new Adhoc::Command() );
235 }
236
238 {
239 m_adhocTrackMapMutex.lock();
240 m_adhocTrackMap.clear();
241 m_adhocTrackMapMutex.unlock();
242
243 if( !m_parent || !m_parent->disco() )
244 return;
245
247 m_parent->disco()->removeNodeHandler( this, XMLNS_ADHOC_COMMANDS );
248 m_parent->disco()->removeNodeHandler( this, EmptyString );
249 m_parent->removeIqHandler( this, ExtAdhocCommand );
250 m_parent->removeIDHandler( this );
252 }
253
254 StringList Adhoc::handleDiscoNodeFeatures( const JID& /*from*/, const std::string& /*node*/ )
255 {
256 StringList features;
257 features.push_back( XMLNS_ADHOC_COMMANDS );
258 return features;
259// return StringList( 1, XMLNS_ADHOC_COMMANDS );
260 }
261
262 Disco::ItemList Adhoc::handleDiscoNodeItems( const JID& from, const JID& /*to*/, const std::string& node )
263 {
265 if( node.empty() )
266 {
267 l.push_back( new Disco::Item( m_parent->jid(), XMLNS_ADHOC_COMMANDS, "Ad-Hoc Commands" ) );
268 }
269 else if( node == XMLNS_ADHOC_COMMANDS )
270 {
271 StringMap::const_iterator it = m_items.begin();
272 for( ; it != m_items.end(); ++it )
273 {
274 AdhocCommandProviderMap::const_iterator itp = m_adhocCommandProviders.find( (*it).first );
275 if( itp != m_adhocCommandProviders.end()
276 && (*itp).second
277 && (*itp).second->handleAdhocAccessRequest( from, (*it).first ) )
278 {
279 l.push_back( new Disco::Item( m_parent->jid(), (*it).first, (*it).second ) );
280 }
281 }
282 }
283 return l;
284 }
285
286 Disco::IdentityList Adhoc::handleDiscoNodeIdentities( const JID& /*from*/, const std::string& node )
287 {
289 StringMap::const_iterator it = m_items.find( node );
290 l.push_back( new Disco::Identity( "automation",
291 node == XMLNS_ADHOC_COMMANDS ? "command-list" : "command-node",
292 it == m_items.end() ? "Ad-Hoc Commands" : (*it).second ) );
293 return l;
294 }
295
296 bool Adhoc::handleIq( const IQ& iq )
297 {
298 if( iq.subtype() != IQ::Set )
299 return false;
300
302 if( !ac || ac->node().empty())
303 return false;
304
305 AdhocCommandProviderMap::const_iterator it = m_adhocCommandProviders.find( ac->node() );
306 if( it != m_adhocCommandProviders.end() )
307 {
308 const std::string& sess = ac->sessionID().empty() ? m_parent->getID() : ac->sessionID();
309 m_activeSessions[sess] = iq.id();
310 (*it).second->handleAdhocCommand( iq.from(), *ac, sess );
311 return true;
312 }
313
314 return false;
315 }
316
317 void Adhoc::handleIqID( const IQ& iq, int context )
318 {
319 if( context != ExecuteAdhocCommand )
320 return;
321
322 m_adhocTrackMapMutex.lock();
323 AdhocTrackMap::iterator it = m_adhocTrackMap.find( iq.id() );
324 bool haveIdHandler = ( it != m_adhocTrackMap.end() );
325 m_adhocTrackMapMutex.unlock();
326 if( !haveIdHandler || (*it).second.context != context
327 || (*it).second.remote != iq.from() )
328 return;
329
330 switch( iq.subtype() )
331 {
332 case IQ::Error:
333 (*it).second.ah->handleAdhocError( iq.from(), iq.error(), (*it).second.handlerContext );
334 break;
335 case IQ::Result:
336 {
338 if( ac )
339 (*it).second.ah->handleAdhocExecutionResult( iq.from(), *ac, (*it).second.handlerContext );
340 break;
341 }
342 default:
343 break;
344 }
345 m_adhocTrackMapMutex.lock();
346 m_adhocTrackMap.erase( it );
347 m_adhocTrackMapMutex.unlock();
348 }
349
350 void Adhoc::registerAdhocCommandProvider( AdhocCommandProvider* acp, const std::string& command,
351 const std::string& name )
352 {
353 if( !m_parent || !m_parent->disco() )
354 return;
355
356 m_parent->disco()->registerNodeHandler( this, command );
357 m_adhocCommandProviders[command] = acp;
358 m_items[command] = name;
359 }
360
361 void Adhoc::handleDiscoInfo( const JID& from, const Disco::Info& info, int context )
362 {
363 if( context != CheckAdhocSupport )
364 return;
365
366 util::MutexGuard m( m_adhocTrackMapMutex );
367
368 AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
369 for( ; it != m_adhocTrackMap.end() && (*it).second.context != context
370 && (*it).second.remote != from; ++it )
371 ;
372 if( it == m_adhocTrackMap.end() )
373 return;
374
375 (*it).second.ah->handleAdhocSupport( from, info.hasFeature( XMLNS_ADHOC_COMMANDS ), (*it).second.handlerContext );
376 m_adhocTrackMap.erase( it );
377 }
378
379 void Adhoc::handleDiscoItems( const JID& from, const Disco::Items& items, int context )
380 {
381 if( context != FetchAdhocCommands )
382 return;
383
384 util::MutexGuard m( m_adhocTrackMapMutex );
385
386 AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
387 for( ; it != m_adhocTrackMap.end(); ++it )
388 {
389 if( (*it).second.context == context && (*it).second.remote == from )
390 {
391 StringMap commands;
392 const Disco::ItemList& l = items.items();
393 Disco::ItemList::const_iterator it2 = l.begin();
394 for( ; it2 != l.end(); ++it2 )
395 {
396 commands[(*it2)->node()] = (*it2)->name();
397 }
398 (*it).second.ah->handleAdhocCommands( from, commands, (*it).second.handlerContext );
399
400 m_adhocTrackMap.erase( it );
401 break;
402 }
403 }
404 }
405
406 void Adhoc::handleDiscoError( const JID& from, const Error* error, int context )
407 {
408 util::MutexGuard m( m_adhocTrackMapMutex );
409 for( AdhocTrackMap::iterator it = m_adhocTrackMap.begin(); it != m_adhocTrackMap.end(); )
410 {
411 if( (*it).second.context == context && (*it).second.remote == from )
412 {
413 (*it).second.ah->handleAdhocError( from, error, (*it).second.handlerContext );
414
415 // Normally we'd just assign it to the return value of the .erase() call,
416 // which is either the next element, or .end(). However,
417 // it's only since C++11 that this works; C++03 version returns void.
418 // So instead, we do a post-increment. this increments the iterator to point
419 // to the next element, then passes a copy of the old iterator (that is to the item to be deleted)
420 m_adhocTrackMap.erase( it++ );
421 }
422 else
423 {
424 ++it;
425 }
426 }
427 }
428
429 void Adhoc::checkSupport( const JID& remote, AdhocHandler* ah, int context )
430 {
431 if( !remote || !ah || !m_parent || !m_parent->disco() )
432 return;
433
434 TrackStruct track;
435 track.remote = remote;
436 track.context = CheckAdhocSupport;
437 track.ah = ah;
438 track.handlerContext = context;
439 const std::string& id = m_parent->getID();
440 m_adhocTrackMapMutex.lock();
441 m_adhocTrackMap[id] = track;
442 m_adhocTrackMapMutex.unlock();
443 m_parent->disco()->getDiscoInfo( remote, EmptyString, this, CheckAdhocSupport, id );
444 }
445
446 void Adhoc::getCommands( const JID& remote, AdhocHandler* ah, int context )
447 {
448 if( !remote || !ah || !m_parent || !m_parent->disco() )
449 return;
450
451 TrackStruct track;
452 track.remote = remote;
453 track.context = FetchAdhocCommands;
454 track.ah = ah;
455 track.handlerContext = context;
456 const std::string& id = m_parent->getID();
457 m_adhocTrackMapMutex.lock();
458 m_adhocTrackMap[id] = track;
459 m_adhocTrackMapMutex.unlock();
460 m_parent->disco()->getDiscoItems( remote, XMLNS_ADHOC_COMMANDS, this, FetchAdhocCommands, id );
461 }
462
463 void Adhoc::execute( const JID& remote, const Adhoc::Command* command, AdhocHandler* ah, int context )
464 {
465 if( !remote || !command || !m_parent || !ah )
466 return;
467
468 const std::string& id = m_parent->getID();
469 IQ iq( IQ::Set, remote, id );
470 iq.addExtension( command );
471
472 TrackStruct track;
473 track.remote = remote;
474 track.context = ExecuteAdhocCommand;
475 track.session = command->sessionID();
476 track.ah = ah;
477 track.handlerContext = context;
478 m_adhocTrackMapMutex.lock();
479 m_adhocTrackMap[id] = track;
480 m_adhocTrackMapMutex.unlock();
481
482 m_parent->send( iq, this, ExecuteAdhocCommand );
483 }
484
485 void Adhoc::respond( const JID& remote, const Adhoc::Command* command, const Error* error )
486 {
487 if( !remote || !command || !m_parent )
488 return;
489
490 StringMap::iterator it = m_activeSessions.find( command->sessionID() );
491 if( it == m_activeSessions.end() )
492 return;
493
494 IQ re( error ? IQ::Error : IQ::Result, remote, (*it).second );
495 re.addExtension( command );
496 if( error )
497 re.addExtension( error );
498 m_parent->send( re );
499 m_activeSessions.erase( it );
500 }
501
502 void Adhoc::removeAdhocCommandProvider( const std::string& command )
503 {
504 if( !m_parent || !m_parent->disco() )
505 return;
506
507 m_parent->disco()->removeNodeHandler( this, command );
508 m_adhocCommandProviders.erase( command );
509 m_items.erase( command );
510 }
511
512}
A virtual interface for an Ad-hoc Command Provider according to XEP-0050.
A virtual interface for an Ad-hoc Command users according to XEP-0050.
A base class for Adhoc Command plugins (DataForm, IO Data, ...).
Definition adhocplugin.h:39
Note(Severity sev, const std::string &note)
Definition adhoc.h:155
An abstraction of an Adhoc Command element (from Adhoc Commands, XEP-0050) as a StanzaExtension.
Definition adhoc.h:92
const std::string & node() const
Definition adhoc.h:268
Command(const std::string &node, const std::string &sessionid, Action action, AdhocPlugin *plugin=0)
Definition adhoc.cpp:97
const AdhocPlugin * plugin() const
Definition adhoc.h:320
virtual const std::string & filterString() const
Definition adhoc.cpp:162
virtual Tag * tag() const
Definition adhoc.cpp:168
const std::string & sessionID() const
Definition adhoc.h:274
Action action() const
Definition adhoc.h:287
virtual Disco::ItemList handleDiscoNodeItems(const JID &from, const JID &to, const std::string &node)
Definition adhoc.cpp:262
virtual void handleIqID(const IQ &iq, int context)
Definition adhoc.cpp:317
virtual bool handleIq(const IQ &iq)
Definition adhoc.cpp:296
virtual void handleDiscoInfo(const JID &from, const Disco::Info &info, int context)
Definition adhoc.cpp:361
void registerAdhocCommandProvider(AdhocCommandProvider *acp, const std::string &command, const std::string &name)
Definition adhoc.cpp:350
virtual ~Adhoc()
Definition adhoc.cpp:237
void respond(const JID &remote, const Adhoc::Command *command, const Error *error=0)
Definition adhoc.cpp:485
virtual void handleDiscoError(const JID &from, const Error *error, int context)
Definition adhoc.cpp:406
void getCommands(const JID &remote, AdhocHandler *ah, int context=0)
Definition adhoc.cpp:446
void removeAdhocCommandProvider(const std::string &command)
Definition adhoc.cpp:502
virtual void handleDiscoItems(const JID &from, const Disco::Items &items, int context)
Definition adhoc.cpp:379
void checkSupport(const JID &remote, AdhocHandler *ah, int context=0)
Definition adhoc.cpp:429
void execute(const JID &remote, const Adhoc::Command *command, AdhocHandler *ah, int context=0)
Definition adhoc.cpp:463
virtual Disco::IdentityList handleDiscoNodeIdentities(const JID &from, const std::string &node)
Definition adhoc.cpp:286
virtual StringList handleDiscoNodeFeatures(const JID &from, const std::string &node)
Definition adhoc.cpp:254
Adhoc(ClientBase *parent)
Definition adhoc.cpp:224
This is the common base class for a Jabber/XMPP Client and a Jabber Component.
Definition clientbase.h:79
const std::string getID()
void removeIqHandler(IqHandler *ih, int exttype)
void removeIDHandler(IqHandler *ih)
bool removeStanzaExtension(int ext)
void send(Tag *tag)
const JID & jid()
Definition clientbase.h:147
void registerIqHandler(IqHandler *ih, int exttype)
virtual Disco * disco() const
Definition clientbase.h:233
void registerStanzaExtension(StanzaExtension *ext)
An abstraction of a XEP-0004 Data Form.
Definition dataform.h:57
An abstraction of a Disco identity (Service Discovery, XEP-0030).
Definition disco.h:197
An abstraction of a Disco Info element (from Service Discovery, XEP-0030) as a StanzaExtension.
Definition disco.h:66
bool hasFeature(const std::string &feature) const
Definition disco.cpp:121
An abstraction of a Disco item (Service Discovery, XEP-0030).
Definition disco.h:353
An abstraction of a Disco query element (from Service Discovery, XEP-0030) in the disco::items namesp...
Definition disco.h:276
const ItemList & items() const
Definition disco.h:311
void removeNodeHandler(DiscoNodeHandler *nh, const std::string &node)
Definition disco.cpp:502
void getDiscoInfo(const JID &to, const std::string &node, DiscoHandler *dh, int context, const std::string &tid=EmptyString)
Definition disco.h:451
void registerNodeHandler(DiscoNodeHandler *nh, const std::string &node)
Definition disco.cpp:497
void removeFeature(const std::string &feature)
Definition disco.h:430
void addFeature(const std::string &feature)
Definition disco.h:422
std::list< Identity * > IdentityList
Definition disco.h:56
void getDiscoItems(const JID &to, const std::string &node, DiscoHandler *dh, int context, const std::string &tid=EmptyString)
Definition disco.h:466
std::list< Item * > ItemList
Definition disco.h:266
A stanza error abstraction implemented as a StanzaExtension.
Definition error.h:35
This is an abstraction of the IO Data specification XEP-0244.
Definition iodata.h:37
An abstraction of an IQ stanza.
Definition iq.h:34
IqType subtype() const
Definition iq.h:74
@ Set
Definition iq.h:46
@ Error
Definition iq.h:49
@ Result
Definition iq.h:48
An abstraction of a JID.
Definition jid.h:31
This class abstracts a stanza extension, which is usually an XML child element in a specific namespac...
void addExtension(const StanzaExtension *se)
Definition stanza.cpp:52
const std::string & id() const
Definition stanza.h:63
const Error * error() const
Definition stanza.cpp:47
const JID & from() const
Definition stanza.h:51
const StanzaExtension * findExtension(int type) const
Definition stanza.cpp:57
This is an abstraction of an XML element.
Definition tag.h:47
Tag * findChild(const std::string &name) const
Definition tag.cpp:624
const std::string & name() const
Definition tag.h:394
const std::string xmlns() const
Definition tag.cpp:543
bool addAttribute(Attribute *attr)
Definition tag.cpp:354
bool hasChild(const std::string &name, const std::string &attr=EmptyString, const std::string &value=EmptyString) const
Definition tag.cpp:615
ConstTagList findTagList(const std::string &expression) const
Definition tag.cpp:811
void addChild(Tag *child)
Definition tag.cpp:424
const std::string cdata() const
Definition tag.cpp:497
const std::string & findAttribute(const std::string &name) const
Definition tag.cpp:589
bool setXmlns(const std::string &xmlns, const std::string &prefix=EmptyString)
Definition tag.cpp:522
A simple implementation of a mutex guard.
Definition mutexguard.h:32
void clearList(std::list< T * > &L)
Definition util.h:152
The namespace for the gloox library.
Definition adhoc.cpp:28
std::list< std::string > StringList
Definition gloox.h:1251
const std::string XMLNS_X_DATA
Definition gloox.cpp:49
const std::string EmptyString
Definition gloox.cpp:124
const std::string TYPE
Definition gloox.cpp:123
std::list< const Tag * > ConstTagList
Definition tag.h:36
const std::string XMLNS_IODATA
Definition gloox.cpp:114
const std::string XMLNS_ADHOC_COMMANDS
Definition gloox.cpp:26
@ ExtAdhocCommand
std::map< std::string, std::string > StringMap
Definition gloox.h:1261