gloox  1.0
mucroom.cpp
1 /*
2  Copyright (c) 2006-2009 by Jakob Schroeter <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 
15 #include "mucroom.h"
16 #include "clientbase.h"
17 #include "dataform.h"
18 #include "presence.h"
19 #include "disco.h"
20 #include "mucmessagesession.h"
21 #include "message.h"
22 #include "error.h"
23 #include "util.h"
24 #include "tag.h"
25 
26 namespace gloox
27 {
28 
29  // ---- MUCRoom::MUCAdmin ----
30  /* Error type values */
31  static const char* affiliationValues [] = {
32  "none",
33  "outcast",
34  "member",
35  "owner",
36  "admin"
37  };
38 
39  /* Stanza error values */
40  static const char* roleValues [] = {
41  "none",
42  "visitor",
43  "participant",
44  "moderator",
45  };
46 
48  const char* historyTypeValues[] =
49  {
50  "maxchars", "maxstanzas", "seconds", "since"
51  };
52 
53  static inline MUCRoomAffiliation affiliationType( const std::string& type )
54  {
55  return (MUCRoomAffiliation)util::lookup( type, affiliationValues );
56  }
57 
58  static inline MUCRoomRole roleType( const std::string& type )
59  {
60  return (MUCRoomRole)util::lookup( type, roleValues );
61  }
62 
63  MUCRoom::MUCAdmin::MUCAdmin( MUCRoomRole role, const std::string& nick,
64  const std::string& reason )
65  : StanzaExtension( ExtMUCAdmin ), m_affiliation( AffiliationInvalid ), m_role( role )
66  {
67  m_list.push_back( MUCListItem( nick, role, reason ) );
68  }
69 
70  MUCRoom::MUCAdmin::MUCAdmin( MUCRoomAffiliation affiliation, const std::string& nick,
71  const std::string& reason )
72  : StanzaExtension( ExtMUCAdmin ), m_affiliation( affiliation ), m_role( RoleInvalid )
73  {
74  m_list.push_back( MUCListItem( nick, affiliation, reason ) );
75  }
76 
77  MUCRoom::MUCAdmin::MUCAdmin( MUCOperation operation, const MUCListItemList& jids )
78  : StanzaExtension( ExtMUCAdmin ), m_list( jids ), m_affiliation( AffiliationInvalid ),
79  m_role( RoleInvalid )
80  {
81  switch( operation )
82  {
83  case StoreVoiceList:
84  case RequestVoiceList:
85  m_role = RoleParticipant;
86  break;
87  case StoreModeratorList:
89  m_role = RoleModerator;
90  break;
91  case StoreBanList:
92  case RequestBanList:
93  m_affiliation = AffiliationOutcast;
94  break;
95  case StoreMemberList:
96  case RequestMemberList:
97  m_affiliation = AffiliationMember;
98  break;
99  case StoreOwnerList:
100  case RequestOwnerList:
101  m_affiliation = AffiliationOwner;
102  break;
103  case StoreAdminList:
104  case RequestAdminList:
105  m_affiliation = AffiliationAdmin;
106  break;
107  default:
108  return;
109  break;
110  }
111 
112  if( m_list.empty() )
113  m_list.push_back( MUCListItem( JID() ) );
114  }
115 
116  MUCRoom::MUCAdmin::MUCAdmin( const Tag* tag )
117  : StanzaExtension( ExtMUCAdmin ), m_affiliation( AffiliationInvalid ), m_role( RoleInvalid )
118  {
119  if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_MUC_ADMIN )
120  return;
121 
122  const TagList& items = tag->findChildren( "item" );
123  TagList::const_iterator it = items.begin();
124  for( ; it != items.end(); ++it )
125  {
126  m_list.push_back( MUCListItem( JID( (*it)->findAttribute( "jid" ) ),
127  roleType( (*it)->findAttribute( "role" ) ),
128  affiliationType( (*it)->findAttribute( "affiliation" ) ),
129  (*it)->findAttribute( "nick" ) ) );
130  if( m_role == RoleInvalid )
131  m_role = roleType( (*it)->findAttribute( "role" ) );
132  if( m_affiliation == AffiliationInvalid )
133  m_affiliation = affiliationType( (*it)->findAttribute( "affiliation" ) );
134  }
135  }
136 
137  MUCRoom::MUCAdmin::~MUCAdmin()
138  {
139  }
140 
141  const std::string& MUCRoom::MUCAdmin::filterString() const
142  {
143  static const std::string filter = "/iq/query[@xmlns='" + XMLNS_MUC_ADMIN + "']";
144  return filter;
145  }
146 
147  Tag* MUCRoom::MUCAdmin::tag() const
148  {
149  Tag* t = new Tag( "query" );
150  t->setXmlns( XMLNS_MUC_ADMIN );
151 
152  if( m_list.empty() || ( m_affiliation == AffiliationInvalid && m_role == RoleInvalid ) )
153  return t;
154 
155  MUCListItemList::const_iterator it = m_list.begin();
156  for( ; it != m_list.end(); ++it )
157  {
158  Tag* i = new Tag( t, "item" );
159  if( (*it).jid() )
160  i->addAttribute( "jid", (*it).jid().bare() );
161  if( !(*it).nick().empty() )
162  i->addAttribute( "nick", (*it).nick() );
163 
164  MUCRoomRole rol = RoleInvalid;
165  if( (*it).role() != RoleInvalid )
166  rol = (*it).role();
167  else if( m_role != RoleInvalid )
168  rol = m_role;
169  if( rol != RoleInvalid )
170  i->addAttribute( "role", util::lookup( rol, roleValues ) );
171 
173  if( (*it).affiliation() != AffiliationInvalid )
174  aff = (*it).affiliation();
175  else if( m_affiliation != AffiliationInvalid )
176  aff = m_affiliation;
177  if( aff != AffiliationInvalid )
178  i->addAttribute( "affiliation", util::lookup( aff, affiliationValues ) );
179  if( !(*it).reason().empty() )
180  new Tag( i, "reason", (*it).reason() );
181  }
182 
183  return t;
184  }
185  // ---- ~MUCRoom::MUCAdmin ----
186 
187  // ---- MUCRoom::MUCOwner ----
188  MUCRoom::MUCOwner::MUCOwner( QueryType type, DataForm* form )
189  : StanzaExtension( ExtMUCOwner ), m_type( type ), m_form( form )
190  {
191  m_valid = true;
192 
193  if( m_form )
194  return;
195 
196  switch( type )
197  {
198  case TypeCancelConfig:
199  m_form = new DataForm( TypeCancel );
200  break;
201  case TypeInstantRoom:
202  m_form = new DataForm( TypeSubmit );
203  break;
204  default:
205  break;
206  }
207  }
208 
209  MUCRoom::MUCOwner::MUCOwner( const JID& alternate, const std::string& reason,
210  const std::string& password )
211  : StanzaExtension( ExtMUCOwner ), m_type( TypeDestroy ), m_jid( alternate ),
212  m_reason( reason ), m_pwd( password ), m_form( 0 )
213  {
214  m_valid = true;
215  }
216 
217  MUCRoom::MUCOwner::MUCOwner( const Tag* tag )
218  : StanzaExtension( ExtMUCOwner ), m_type( TypeIncomingTag ), m_form( 0 )
219  {
220  if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_MUC_OWNER )
221  return;
222 
223  const TagList& l = tag->children();
224  TagList::const_iterator it = l.begin();
225  for( ; it != l.end(); ++it )
226  {
227  const std::string& name = (*it)->name();
228  if( name == "x" && (*it)->xmlns() == XMLNS_X_DATA )
229  {
230  m_form = new DataForm( (*it) );
231  break;
232  }
233  else if( name == "destroy" )
234  {
235  m_type = TypeDestroy;
236  m_jid = (*it)->findAttribute( "jid" );
237  m_pwd = (*it)->findCData( "/query/destroy/password" );
238  m_reason = (*it)->findCData( "/query/destroy/reason" );
239  break;
240  }
241  }
242  m_valid = true;
243  }
244 
245  MUCRoom::MUCOwner::~MUCOwner()
246  {
247  delete m_form;
248  }
249 
250  const std::string& MUCRoom::MUCOwner::filterString() const
251  {
252  static const std::string filter = "/iq/query[@xmlns='" + XMLNS_MUC_OWNER + "']";
253  return filter;
254  }
255 
256  Tag* MUCRoom::MUCOwner::tag() const
257  {
258  if( !m_valid )
259  return 0;
260 
261  Tag* t = new Tag( "query" );
262  t->setXmlns( XMLNS_MUC_OWNER );
263 
264  switch( m_type )
265  {
266  case TypeInstantRoom:
267  case TypeSendConfig:
268  case TypeCancelConfig:
269  case TypeIncomingTag:
270  if( m_form )
271  t->addChild( m_form->tag() );
272  break;
273  case TypeDestroy:
274  {
275  Tag* d = new Tag( t, "destroy" );
276  if( m_jid )
277  d->addAttribute( "jid", m_jid.bare() );
278 
279  if( !m_reason.empty() )
280  new Tag( d, "reason", m_reason );
281 
282  if( !m_pwd.empty() )
283  new Tag( d, "password", m_pwd );
284 
285  break;
286  }
287  case TypeRequestConfig:
288  case TypeCreate:
289  default:
290  break;
291  }
292 
293  return t;
294  }
295  // ---- ~MUCRoom::MUCOwner ----
296 
297  // ---- MUCRoom::MUCUser ----
298  MUCRoom::MUCUser::MUCUser( MUCUserOperation operation, const std::string& to,
299  const std::string& reason, const std::string& thread )
300  : StanzaExtension( ExtMUCUser ), m_affiliation( AffiliationInvalid ), m_role( RoleInvalid ),
301  m_jid( new std::string( to ) ), m_actor( 0 ),
302  m_thread( thread.empty() ? 0 : new std::string( thread ) ),
303  m_reason( new std::string( reason ) ), m_newNick( 0 ), m_password( 0 ), m_alternate( 0 ),
304  m_operation( operation ),
305  m_flags( 0 ), m_del( false ), m_continue( !thread.empty() )
306  {
307  }
308 
310  : StanzaExtension( ExtMUCUser ), m_affiliation( AffiliationInvalid ), m_role( RoleInvalid ),
311  m_jid( 0 ), m_actor( 0 ), m_thread( 0 ), m_reason( 0 ), m_newNick( 0 ),
312  m_password( 0 ), m_alternate( 0 ), m_operation( OpNone ),
313  m_flags( 0 ), m_del( false ), m_continue( false )
314  {
315  if( !tag || tag->name() != "x" || tag->xmlns() != XMLNS_MUC_USER )
316  return;
317 
318  const Tag* t = 0;
319  const TagList& l = tag->children();
320  TagList::const_iterator it = l.begin();
321  for( ; it != l.end(); ++it )
322  {
323  if( (*it)->name() == "item" )
324  {
325  m_affiliation = getEnumAffiliation( (*it)->findAttribute( "affiliation" ) );
326  m_role = getEnumRole( (*it)->findAttribute( "role" ) );
327 
328  if( (*it)->hasAttribute( "jid" ) )
329  m_jid = new std::string( (*it)->findAttribute( "jid" ) );
330 
331  if( ( t = (*it)->findChild( "actor" ) ) )
332  m_actor = new std::string( t->findAttribute( "jid" ) );
333 
334  if( ( t = (*it)->findChild( "reason" ) ) )
335  m_reason = new std::string( t->cdata() );
336 
337  if( (*it)->hasAttribute( "nick" ) )
338  m_newNick = new std::string( (*it)->findAttribute( "nick" ) );
339  }
340  else if( (*it)->name() == "status" )
341  {
342  const std::string& code = (*it)->findAttribute( "code" );
343  if( code == "100" )
344  m_flags |= FlagNonAnonymous;
345  else if( code == "101" )
346  m_flags |= UserAffiliationChangedWNR;
347  else if( code == "110" )
348  m_flags |= UserSelf;
349  else if( code == "170" )
350  m_flags |= FlagPublicLogging;
351  else if( code == "201" )
352  m_flags |= UserNewRoom;
353  else if( code == "210" )
354  m_flags |= UserNickAssigned;
355  else if( code == "301" )
356  m_flags |= UserBanned;
357  else if( code == "303" )
358  m_flags |= UserNickChanged;
359  else if( code == "307" )
360  m_flags |= UserKicked;
361  else if( code == "321" )
362  m_flags |= UserAffiliationChanged;
363  else if( code == "322" )
364  m_flags |= UserMembershipRequired;
365  else if( code == "332" )
366  m_flags |= UserRoomShutdown;
367  }
368  else if( (*it)->name() == "destroy" )
369  {
370  m_del = true;
371  if( (*it)->hasAttribute( "jid" ) )
372  m_alternate = new std::string( (*it)->findAttribute( "jid" ) );
373 
374  if( ( t = (*it)->findChild( "reason" ) ) )
375  m_reason = new std::string( t->cdata() );
376 
377  m_flags |= UserRoomDestroyed;
378  }
379  else if( (*it)->name() == "invite" )
380  {
381  m_operation = OpInviteFrom;
382  m_jid = new std::string( (*it)->findAttribute( "from" ) );
383  if( m_jid->empty() )
384  {
385  m_operation = OpInviteTo;
386  m_jid->assign( (*it)->findAttribute( "to" ) );
387  }
388  if( (*it)->hasChild( "reason" ) )
389  m_reason = new std::string( (*it)->findChild( "reason" )->cdata() );
390  if( (*it)->hasChild( "continue" ) )
391  {
392  m_continue = true;
393  m_thread = new std::string( (*it)->findChild( "continue" )->findAttribute( "thread" ) );
394  }
395  }
396  else if( (*it)->name() == "decline" )
397  {
398  m_operation = OpDeclineFrom;
399  m_jid = new std::string( (*it)->findAttribute( "from" ) );
400  if( m_jid->empty() )
401  {
402  m_operation = OpDeclineTo;
403  m_jid->assign( (*it)->findAttribute( "from" ) );
404  }
405  if( (*it)->hasChild( "reason" ) )
406  m_reason = new std::string( (*it)->findChild( "reason" )->cdata() );
407  }
408  else if( (*it)->name() == "password" )
409  {
410  m_password = new std::string( (*it)->cdata() );
411  }
412  }
413  }
414 
416  {
417  delete m_jid;
418  delete m_actor;
419  delete m_thread;
420  delete m_reason;
421  delete m_newNick;
422  delete m_password;
423  delete m_alternate;
424  }
425 
426  MUCRoomRole MUCRoom::MUCUser::getEnumRole( const std::string& role )
427  {
428  if( role == "moderator" )
429  return RoleModerator;
430  if( role == "participant" )
431  return RoleParticipant;
432  if( role == "visitor" )
433  return RoleVisitor;
434  return RoleNone;
435  }
436 
437  MUCRoomAffiliation MUCRoom::MUCUser::getEnumAffiliation( const std::string& affiliation )
438  {
439  if( affiliation == "owner" )
440  return AffiliationOwner;
441  if( affiliation == "admin" )
442  return AffiliationAdmin;
443  if( affiliation == "member" )
444  return AffiliationMember;
445  if( affiliation == "outcast" )
446  return AffiliationOutcast;
447  return AffiliationNone;
448  }
449 
450  const std::string& MUCRoom::MUCUser::filterString() const
451  {
452  static const std::string filter = "/presence/x[@xmlns='" + XMLNS_MUC_USER + "']"
453  "|/message/x[@xmlns='" + XMLNS_MUC_USER + "']";
454  return filter;
455  }
456 
458  {
459  Tag* t = new Tag( "x" );
460  t->setXmlns( XMLNS_MUC_USER );
461 
462  if( m_affiliation != AffiliationInvalid || m_role != RoleInvalid )
463  {
464  Tag* i = new Tag( t, "item" );
465  if( m_jid )
466  i->addAttribute( "jid", *m_jid );
467  if( m_role != RoleInvalid )
468  i->addAttribute( "role", util::lookup( m_role, roleValues ) );
469  if( m_affiliation != AffiliationInvalid )
470  i->addAttribute( "affiliation", util::lookup( m_affiliation, affiliationValues ) );
471 
472  if( m_actor )
473  new Tag( i, "actor", "jid", *m_actor );
474 
475  if( m_flags & FlagNonAnonymous )
476  new Tag( t, "status", "code", "100" );
477  if( m_flags & UserAffiliationChangedWNR )
478  new Tag( t, "status", "code", "101" );
479  if( m_flags & UserSelf )
480  new Tag( t, "status", "code", "110" );
481  if( m_flags & FlagPublicLogging )
482  new Tag( t, "status", "code", "170" );
483  if( m_flags & UserNewRoom )
484  new Tag( t, "status", "code", "201" );
485  if( m_flags & UserNickAssigned )
486  new Tag( t, "status", "code", "210" );
487  if( m_flags & UserBanned )
488  new Tag( t, "status", "code", "301" );
489  if( m_flags & UserNickChanged )
490  new Tag( t, "status", "code", "303" );
491  if( m_flags & UserKicked )
492  new Tag( t, "status", "code", "307" );
493  if( m_flags & UserAffiliationChanged )
494  new Tag( t, "status", "code", "321" );
495  if( m_flags & UserMembershipRequired )
496  new Tag( t, "status", "code", "322" );
497  if( m_flags & UserRoomShutdown )
498  new Tag( t, "status", "code", "332" );
499  }
500  else if( m_del )
501  {
502  Tag* d = new Tag( t, "destroy" );
503  if( m_alternate )
504  d->addAttribute( "jid", *m_alternate );
505  if( m_reason )
506  new Tag( d, "reason", *m_reason );
507  }
508  else if( m_operation != OpNone && m_jid )
509  {
510  Tag* d = 0;
511  if( m_operation == OpInviteTo )
512  d = new Tag( t, "invite", "to", *m_jid );
513  else if( m_operation == OpInviteFrom )
514  d = new Tag( t, "invite", "from", *m_jid );
515  else if( m_operation == OpDeclineTo )
516  d = new Tag( t, "decline", "to", *m_jid );
517  else if( m_operation == OpDeclineFrom )
518  d = new Tag( t, "decline", "from", *m_jid );
519 
520  if( m_reason )
521  new Tag( d, "reason", *m_reason );
522 
523  if( m_continue )
524  {
525  Tag* c = new Tag( d, "continue" );
526  if( m_thread )
527  c->addAttribute( "thread", *m_thread );
528  }
529 
530  if( m_password )
531  new Tag( t, "password", *m_password );
532 
533  }
534 
535  return t;
536  }
537  // ---- ~MUCRoom::MUCUser ----
538 
539  // ---- MUCRoom::MUC ----
540  MUCRoom::MUC::MUC( const std::string& password,
541  MUCRoom::HistoryRequestType historyType,
542  const std::string& historySince,
543  int historyValue )
544  : StanzaExtension( ExtMUC ),
545  m_password( password.empty() ? 0 : new std::string( password ) ),
546  m_historySince( new std::string( historySince ) ),
547  m_historyType( historyType ), m_historyValue( historyValue )
548  {
549  }
550 
551  MUCRoom::MUC::MUC( const Tag* tag )
552  : StanzaExtension( ExtMUC ),
553  m_password( 0 ), m_historySince( 0 ),
554  m_historyType( HistoryUnknown ), m_historyValue( 0 )
555  {
556  if( !tag || tag->name() != "x" || tag->xmlns() != XMLNS_MUC_USER )
557  return;
558 
559  const TagList& l = tag->children();
560  TagList::const_iterator it = l.begin();
561  for( ; it != l.end(); ++it )
562  {
563  if( (*it)->name() == "history" )
564  {
565  if( (*it)->hasAttribute( "seconds" ) )
566  m_historyValue = atoi( (*it)->findAttribute( "seconds" ).c_str() );
567  else if( (*it)->hasAttribute( "maxstanzas" ) )
568  m_historyValue = atoi( (*it)->findAttribute( "maxstanzas" ).c_str() );
569  else if( (*it)->hasAttribute( "maxchars" ) )
570  m_historyValue = atoi( (*it)->findAttribute( "maxchars" ).c_str() );
571  else if( (*it)->hasAttribute( "since" ) )
572  m_historySince = new std::string( (*it)->findAttribute( "since" ) );
573  }
574  else if( (*it)->name() == "password" )
575  {
576  m_password = new std::string( (*it)->cdata() );
577  }
578  }
579  }
580 
582  {
583  delete m_password;
584  delete m_historySince;
585  }
586 
587  const std::string& MUCRoom::MUC::filterString() const
588  {
589  static const std::string filter = "/presence/x[@xmlns='" + XMLNS_MUC + "']";
590  return filter;
591  }
592 
594  {
595  Tag* t = new Tag( "x" );
596  t->setXmlns( XMLNS_MUC );
597 
598  if( m_historyType != HistoryUnknown )
599  {
600  const std::string& histStr = util::lookup( m_historyType, historyTypeValues );
601  Tag* h = new Tag( t, "history" );
602  if( m_historyType == HistorySince && m_historySince )
603  h->addAttribute( histStr, *m_historySince );
604  else
605  h->addAttribute( histStr, m_historyValue );
606  }
607 
608  if( m_password )
609  new Tag( t, "password", *m_password );
610 
611  return t;
612  }
613  // ---- ~MUCRoom::MUC ----
614 
615  // --- MUCRoom ----
616  MUCRoom::MUCRoom( ClientBase* parent, const JID& nick, MUCRoomHandler* mrh,
617  MUCRoomConfigHandler* mrch )
618  : m_parent( parent ), m_nick( nick ), m_joined( false ), m_roomHandler( mrh ),
619  m_roomConfigHandler( mrch ), m_affiliation( AffiliationNone ), m_role( RoleNone ),
620  m_historyType( HistoryUnknown ), m_historyValue( 0 ), m_flags( 0 ),
621  m_creationInProgress( false ), m_configChanged( false ),
622  m_publishNick( false ), m_publish( false ), m_unique( false )
623  {
624  if( m_parent )
625  {
626  m_parent->registerStanzaExtension( new MUCAdmin() );
627  m_parent->registerStanzaExtension( new MUCOwner() );
628  m_parent->registerStanzaExtension( new MUCUser() );
629  m_parent->registerStanzaExtension( new MUC() );
630  m_parent->registerStanzaExtension( new DelayedDelivery() );
631  }
632  }
633 
635  {
636  if( m_joined )
637  leave();
638 
639  if( m_parent )
640  {
641  if( m_publish )
642  m_parent->disco()->removeNodeHandler( this, XMLNS_MUC_ROOMS );
643 
644  m_parent->removeIDHandler( this );
645 // m_parent->removeStanzaExtension( ExtMUCAdmin ); // don't remove, other rooms might need it
646 // m_parent->removeStanzaExtension( ExtMUCOwner );
647  m_parent->removePresenceHandler( m_nick.bareJID(), this );
648  m_parent->disco()->removeDiscoHandler( this );
649  }
650  }
651 
652  void MUCRoom::join( Presence::PresenceType type, const std::string& status, int priority )
653  {
654  if( m_joined || !m_parent )
655  return;
656 
657  m_parent->registerPresenceHandler( m_nick.bareJID(), this );
658 
659  m_session = new MUCMessageSession( m_parent, m_nick.bareJID() );
660  m_session->registerMessageHandler( this );
661 
662  Presence pres( type, m_nick.full(), status, priority );
663  pres.addExtension( new MUC( m_password, m_historyType, m_historySince, m_historyValue ) );
664  m_joined = true;
665  m_parent->send( pres );
666  }
667 
668  void MUCRoom::leave( const std::string& msg )
669  {
670  if( !m_joined )
671  return;
672 
673  if( m_parent )
674  {
675  Presence pres( Presence::Unavailable, m_nick.full(), msg );
676  m_parent->send( pres );
677  m_parent->removePresenceHandler( m_nick.bareJID(), this );
678  m_parent->disposeMessageSession( m_session );
679  }
680 
681  m_session = 0;
682  m_joined = false;
683  }
684 
685  void MUCRoom::destroy( const std::string& reason, const JID& alternate, const std::string& password )
686  {
687  if( !m_parent )
688  return;
689 
690  const std::string& id = m_parent->getID();
691  IQ iq( IQ::Set, m_nick.bareJID(), id );
692  iq.addExtension( new MUCOwner( alternate, reason, password ) );
693  m_parent->send( iq, this, DestroyRoom );
694  }
695 
696  void MUCRoom::send( const std::string& message )
697  {
698  if( m_session && m_joined )
699  m_session->send( message );
700  }
701 
702  void MUCRoom::setSubject( const std::string& subject )
703  {
704  if( m_session && m_joined )
705  m_session->setSubject( subject );
706  }
707 
708  void MUCRoom::setNick( const std::string& nick )
709  {
710  if( m_parent && m_joined )
711  {
712  m_newNick = nick;
713 
714  Presence p( Presence::Available, m_nick.bare() + "/" + m_newNick );
715  m_parent->send( p );
716  }
717  else
718  m_nick.setResource( nick );
719  }
720 
722  {
723  if( m_parent )
724  m_parent->disco()->getDiscoInfo( m_nick.bare(), EmptyString, this, GetRoomInfo );
725  }
726 
728  {
729  if( m_parent )
730  m_parent->disco()->getDiscoItems( m_nick.bare(), EmptyString, this, GetRoomItems );
731  }
732 
733  void MUCRoom::setPresence( Presence::PresenceType presence, const std::string& msg )
734  {
735  if( m_parent && presence != Presence::Unavailable && m_joined )
736  {
737  Presence p( presence, m_nick.full(), msg );
738  m_parent->send( p );
739  }
740  }
741 
742  void MUCRoom::invite( const JID& invitee, const std::string& reason, const std::string& thread )
743  {
744  if( !m_parent || !m_joined )
745  return;
746 
747  Message msg( Message::Normal, m_nick.bareJID() );
748  msg.addExtension( new MUCUser( OpInviteTo, invitee.bare(), reason, thread ) );
749  m_parent->send( msg );
750  }
751 
752  Message* MUCRoom::declineInvitation( const JID& room, const JID& invitor, const std::string& reason )
753  {
754  Message* msg = new Message( Message::Normal, room.bare() );
755  msg->addExtension( new MUCUser( OpDeclineTo, invitor.bare(), reason ) );
756  return msg;
757  }
758 
759  void MUCRoom::setPublish( bool publish, bool publishNick )
760  {
761  m_publish = publish;
762  m_publishNick = publishNick;
763 
764  if( !m_parent )
765  return;
766 
767  if( m_publish )
768  m_parent->disco()->registerNodeHandler( this, XMLNS_MUC_ROOMS );
769  else
770  m_parent->disco()->removeNodeHandler( this, XMLNS_MUC_ROOMS );
771  }
772 
773  void MUCRoom::addHistory( const std::string& message, const JID& from, const std::string& stamp )
774  {
775  if( !m_joined || !m_parent )
776  return;
777 
778  Message m( Message::Groupchat, m_nick.bareJID(), message );
779  m.addExtension( new DelayedDelivery( from, stamp ) );
780  m_parent->send( m );
781  }
782 
784  {
785  m_historyType = type;
786  m_historySince = EmptyString;
787  m_historyValue = value;
788  }
789 
790  void MUCRoom::setRequestHistory( const std::string& since )
791  {
792  m_historyType = HistorySince;
793  m_historySince = since;
794  m_historyValue = 0;
795  }
796 
797  Message* MUCRoom::createDataForm( const JID& room, const DataForm* df )
798  {
799  Message* m = new Message( Message::Normal, room.bare() );
800  m->addExtension( df );
801  return m;
802  }
803 
805  {
806  if( !m_parent || !m_joined )
807  return;
808 
809  DataForm* df = new DataForm( TypeSubmit );
811  df->addField( DataFormField::TypeTextSingle, "muc#role", "participant", "Requested role" );
812 
813  Message m( Message::Normal, m_nick.bare() );
814  m.addExtension( df );
815 
816  m_parent->send( m );
817  }
818 
819  void MUCRoom::setRole( const std::string& nick, MUCRoomRole role,
820  const std::string& reason )
821  {
822  if( !m_parent || !m_joined || nick.empty() || role == RoleInvalid )
823  return;
824 
826  switch( role )
827  {
828  case RoleNone:
829  action = SetRNone;
830  break;
831  case RoleVisitor:
832  action = SetVisitor;
833  break;
834  case RoleParticipant:
835  action = SetParticipant;
836  break;
837  case RoleModerator:
838  action = SetModerator;
839  break;
840  default:
841  break;
842  }
843 
844  IQ iq( IQ::Set, m_nick.bareJID() );
845  iq.addExtension( new MUCAdmin( role, nick, reason ) );
846 
847  m_parent->send( iq, this, action );
848  }
849 
850  void MUCRoom::setAffiliation( const std::string& nick, MUCRoomAffiliation affiliation,
851  const std::string& reason )
852  {
853  if( !m_parent || !m_joined || nick.empty() || affiliation == AffiliationInvalid )
854  return;
855 
857  switch( affiliation )
858  {
859  case AffiliationOutcast:
860  action = SetOutcast;
861  break;
862  case AffiliationNone:
863  action = SetANone;
864  break;
865  case AffiliationMember:
866  action = SetMember;
867  break;
868  case AffiliationAdmin:
869  action = SetAdmin;
870  break;
871  case AffiliationOwner:
872  action = SetOwner;
873  break;
874  default:
875  break;
876  }
877 
878  IQ iq( IQ::Set, m_nick.bareJID() );
879  iq.addExtension( new MUCAdmin( affiliation, nick, reason ) );
880 
881  m_parent->send( iq, this, action );
882  }
883 
885  {
886  if( !m_parent || !m_joined || !m_roomConfigHandler )
887  return;
888 
889  IQ iq( IQ::Get, m_nick.bareJID() );
890  iq.addExtension( new MUCAdmin( operation ) );
891  m_parent->send( iq, this, operation );
892  }
893 
894  void MUCRoom::storeList( const MUCListItemList items, MUCOperation operation )
895  {
896  if( !m_parent || !m_joined )
897  return;
898 
899  IQ iq( IQ::Set, m_nick.bareJID() );
900  iq.addExtension( new MUCAdmin( operation , items ) );
901  m_parent->send( iq, this, operation );
902  }
903 
904  void MUCRoom::handlePresence( const Presence& presence )
905  {
906  if( ( presence.from().bare() != m_nick.bare() ) || !m_roomHandler )
907  return;
908 
909  if( presence.subtype() == Presence::Error )
910  {
911  if( m_newNick.empty() )
912  {
913  m_parent->removePresenceHandler( m_nick.bareJID(), this );
914  m_parent->disposeMessageSession( m_session );
915  m_joined = false;
916  m_session = 0;
917  }
918  else
919  m_newNick = "";
920 
921  m_roomHandler->handleMUCError( this, presence.error()
922  ? presence.error()->error()
924  }
925  else
926  {
927  const MUCUser* mu = presence.findExtension<MUCUser>( ExtMUCUser );
928  if( !mu )
929  return;
930 
931  MUCRoomParticipant party;
932  party.nick = new JID( presence.from() );
933  party.status = presence.status();
934  party.affiliation = mu->affiliation();
935  party.role = mu->role();
936  party.jid = mu->jid() ? new JID( *(mu->jid()) ) : 0;
937  party.actor = mu->actor() ? new JID( *(mu->actor()) ) : 0;
938  party.reason = mu->reason() ? *(mu->reason()) : EmptyString;
939  party.newNick = mu->newNick() ? *(mu->newNick()) : EmptyString;
940  party.alternate = mu->alternate() ? new JID( *(mu->alternate()) ) : 0;
941  party.flags = mu->flags();
942 
943  if( party.flags & FlagNonAnonymous )
944  setNonAnonymous();
945 
946  if( party.flags & UserSelf )
947  {
948  m_role = party.role;
949  m_affiliation = party.affiliation;
950  }
951  if( party.flags & UserNewRoom )
952  {
953  m_creationInProgress = true;
954  if( instantRoomHook() || m_roomHandler->handleMUCRoomCreation( this ) )
956  }
957  if( party.flags & UserNickAssigned )
958  m_nick.setResource( presence.from().resource() );
959 
960  if( party.flags & UserNickChanged && !party.newNick.empty()
961  && m_nick.resource() == presence.from().resource()
962  && party.newNick == m_newNick )
963  party.flags |= UserSelf;
964 
965  if( party.flags & UserNickChanged && party.flags & UserSelf && !party.newNick.empty() )
966  m_nick.setResource( party.newNick );
967 
968  if( m_roomHandler )
969  m_roomHandler->handleMUCParticipantPresence( this, party, presence );
970 
971  delete party.nick;
972  }
973  }
974 
975  void MUCRoom::instantRoom( int context )
976  {
977  if( !m_creationInProgress || !m_parent || !m_joined )
978  return;
979 
980  IQ iq( IQ::Set, m_nick.bareJID() );
981  iq.addExtension( new MUCOwner( context == CreateInstantRoom
982  ? MUCOwner::TypeInstantRoom : MUCOwner::TypeCancelConfig ) );
983 
984  m_parent->send( iq, this, context );
985 
986  m_creationInProgress = false;
987  }
988 
990  {
991  if( !m_parent || !m_joined )
992  return;
993 
994  IQ iq( IQ::Get, m_nick.bareJID() );
995  iq.addExtension( new MUCOwner( MUCOwner::TypeRequestConfig ) );
996 
997  m_parent->send( iq, this, RequestRoomConfig );
998 
999  if( m_creationInProgress )
1000  m_creationInProgress = false;
1001  }
1002 
1004  {
1005  if( !m_parent || !m_joined )
1006  return;
1007 
1008  IQ iq( IQ::Set, m_nick.bareJID() );
1009  iq.addExtension( new MUCOwner( MUCOwner::TypeSendConfig, form ) );
1010 
1011  m_parent->send( iq, this, SendRoomConfig );
1012  }
1013 
1014  void MUCRoom::setNonAnonymous()
1015  {
1016  m_flags |= FlagNonAnonymous;
1017  m_flags &= ~( FlagSemiAnonymous | FlagFullyAnonymous );
1018  }
1019 
1020  void MUCRoom::setSemiAnonymous()
1021  {
1022  m_flags &= ~( FlagNonAnonymous | FlagFullyAnonymous );
1023  m_flags |= FlagSemiAnonymous;
1024  }
1025 
1026  void MUCRoom::setFullyAnonymous()
1027  {
1028  m_flags &= ~( FlagNonAnonymous | FlagSemiAnonymous );
1029  m_flags |= FlagFullyAnonymous;
1030  }
1031 
1032  void MUCRoom::handleMessage( const Message& msg, MessageSession* /*session*/ )
1033  {
1034  if( !m_roomHandler )
1035  return;
1036 
1037  if( msg.subtype() == Message::Error )
1038  {
1039  m_roomHandler->handleMUCError( this, msg.error() ? msg.error()->error() : StanzaErrorUndefined );
1040  }
1041  else
1042  {
1043  const MUCUser* mu = msg.findExtension<MUCUser>( ExtMUCUser );
1044  if( mu )
1045  {
1046  const int flags = mu->flags();
1047  if( flags & FlagNonAnonymous )
1048  setNonAnonymous();
1049  if( flags & FlagPublicLogging )
1050  {
1051  m_flags &= ~FlagPublicLoggingOff;
1052  m_flags |= FlagPublicLogging;
1053  }
1054  if( flags & FlagPublicLoggingOff )
1055  {
1056  m_flags &= ~FlagPublicLogging;
1057  m_flags |= FlagPublicLoggingOff;
1058  }
1059  if( flags & FlagSemiAnonymous )
1060  setSemiAnonymous();
1061  if( flags & FlagFullyAnonymous )
1062  setFullyAnonymous();
1063 
1064  if( mu->operation() == OpDeclineFrom && mu->jid() )
1065  m_roomHandler->handleMUCInviteDecline( this, JID( *(mu->jid()) ),
1066  mu->reason() ? *(mu->reason()) : EmptyString );
1067  }
1068 
1069  const DataForm* df = msg.findExtension<DataForm>( ExtDataForm );
1070  if( m_roomConfigHandler && df )
1071  {
1072  m_roomConfigHandler->handleMUCRequest( this, *df );
1073  return;
1074  }
1075 
1076  if( !msg.subject().empty() )
1077  {
1078  m_roomHandler->handleMUCSubject( this, msg.from().resource(), msg.subject() );
1079  }
1080  else if( !msg.body().empty() )
1081  {
1082  std::string when;
1083  bool privMsg = false;
1084  bool history = false;
1085  if( msg.when() )
1086  {
1087  when = msg.when()->stamp();
1088  history = true;
1089  }
1090  if( msg.subtype() & ( Message::Chat | Message::Normal ) )
1091  privMsg = true;
1092 
1093  m_roomHandler->handleMUCMessage( this, msg, privMsg );
1094  }
1095  }
1096  }
1097 
1098  void MUCRoom::handleIqID( const IQ& iq, int context )
1099  {
1100  if( !m_roomConfigHandler )
1101  return;
1102 
1103  switch( iq.subtype() )
1104  {
1105  case IQ::Result:
1106  handleIqResult( iq, context );
1107  break;
1108  case IQ::Error:
1109  handleIqError( iq, context );
1110  break;
1111  default:
1112  break;
1113  }
1114  }
1115 
1116  void MUCRoom::handleIqResult( const IQ& iq, int context )
1117  {
1118  switch( context )
1119  {
1120  case SetRNone:
1121  case SetVisitor:
1122  case SetParticipant:
1123  case SetModerator:
1124  case SetANone:
1125  case SetOutcast:
1126  case SetMember:
1127  case SetAdmin:
1128  case SetOwner:
1129  case CreateInstantRoom:
1130  case CancelRoomCreation:
1131  case DestroyRoom:
1132  case StoreVoiceList:
1133  case StoreBanList:
1134  case StoreMemberList:
1135  case StoreModeratorList:
1136  case StoreAdminList:
1137  m_roomConfigHandler->handleMUCConfigResult( this, true, (MUCOperation)context );
1138  break;
1139  case RequestRoomConfig:
1140  {
1141  const MUCOwner* mo = iq.findExtension<MUCOwner>( ExtMUCOwner );
1142  if( !mo )
1143  break;
1144 
1145  if( mo->form() )
1146  m_roomConfigHandler->handleMUCConfigForm( this, *(mo->form()) );
1147  break;
1148  }
1149  case RequestVoiceList:
1150  case RequestBanList:
1151  case RequestMemberList:
1152  case RequestModeratorList:
1153  case RequestOwnerList:
1154  case RequestAdminList:
1155  {
1156  const MUCAdmin* ma = iq.findExtension<MUCAdmin>( ExtMUCAdmin );
1157  if( !ma )
1158  break;
1159 
1160  m_roomConfigHandler->handleMUCConfigList( this, ma->list(), (MUCOperation)context );
1161  break;
1162  }
1163  default:
1164  break;
1165  }
1166  }
1167 
1168  void MUCRoom::handleIqError( const IQ& /*iq*/, int context )
1169  {
1170  switch( context )
1171  {
1172  case SetRNone:
1173  case SetVisitor:
1174  case SetParticipant:
1175  case SetModerator:
1176  case SetANone:
1177  case SetOutcast:
1178  case SetMember:
1179  case SetAdmin:
1180  case SetOwner:
1181  case CreateInstantRoom:
1182  case CancelRoomCreation:
1183  case RequestRoomConfig:
1184  case DestroyRoom:
1185  case RequestVoiceList:
1186  case StoreVoiceList:
1187  case RequestBanList:
1188  case StoreBanList:
1189  case RequestMemberList:
1190  case StoreMemberList:
1191  case RequestModeratorList:
1192  case StoreModeratorList:
1193  case RequestOwnerList:
1194  case StoreOwnerList:
1195  case RequestAdminList:
1196  case StoreAdminList:
1197  m_roomConfigHandler->handleMUCConfigResult( this, false, (MUCOperation)context );
1198  break;
1199  }
1200  }
1201 
1202  void MUCRoom::handleDiscoInfo( const JID& /*from*/, const Disco::Info& info, int context )
1203  {
1204  switch( context )
1205  {
1206  case GetRoomInfo:
1207  {
1208  int oldflags = m_flags;
1209  m_flags = 0;
1210  if( oldflags & FlagPublicLogging )
1211  m_flags |= FlagPublicLogging;
1212 
1213  std::string name;
1214  const StringList& l = info.features();
1215  StringList::const_iterator it = l.begin();
1216  for( ; it != l.end(); ++it )
1217  {
1218  if( (*it) == "muc_hidden" )
1219  m_flags |= FlagHidden;
1220  else if( (*it) == "muc_membersonly" )
1221  m_flags |= FlagMembersOnly;
1222  else if( (*it) == "muc_moderated" )
1223  m_flags |= FlagModerated;
1224  else if( (*it) == "muc_nonanonymous" )
1225  setNonAnonymous();
1226  else if( (*it) == "muc_open" )
1227  m_flags |= FlagOpen;
1228  else if( (*it) == "muc_passwordprotected" )
1229  m_flags |= FlagPasswordProtected;
1230  else if( (*it) == "muc_persistent" )
1231  m_flags |= FlagPersistent;
1232  else if( (*it) == "muc_public" )
1233  m_flags |= FlagPublic;
1234  else if( (*it) == "muc_semianonymous" )
1235  setSemiAnonymous();
1236  else if( (*it) == "muc_temporary" )
1237  m_flags |= FlagTemporary;
1238  else if( (*it) == "muc_fullyanonymous" )
1239  setFullyAnonymous();
1240  else if( (*it) == "muc_unmoderated" )
1241  m_flags |= FlagUnmoderated;
1242  else if( (*it) == "muc_unsecured" )
1243  m_flags |= FlagUnsecured;
1244  }
1245 
1246  const Disco::IdentityList& il = info.identities();
1247  if( il.size() )
1248  name = il.front()->name();
1249 
1250  if( m_roomHandler )
1251  m_roomHandler->handleMUCInfo( this, m_flags, name, info.form() );
1252  break;
1253  }
1254  default:
1255  break;
1256  }
1257  }
1258 
1259  void MUCRoom::handleDiscoItems( const JID& /*from*/, const Disco::Items& items, int context )
1260  {
1261  if( !m_roomHandler )
1262  return;
1263 
1264  switch( context )
1265  {
1266  case GetRoomItems:
1267  {
1268  m_roomHandler->handleMUCItems( this, items.items() );
1269  break;
1270  }
1271  default:
1272  break;
1273  }
1274  }
1275 
1276  void MUCRoom::handleDiscoError( const JID& /*from*/, const Error* /*error*/, int context )
1277  {
1278  if( !m_roomHandler )
1279  return;
1280 
1281  switch( context )
1282  {
1283  case GetRoomInfo:
1284  m_roomHandler->handleMUCInfo( this, 0, EmptyString, 0 );
1285  break;
1286  case GetRoomItems:
1287  m_roomHandler->handleMUCItems( this, Disco::ItemList() );
1288  break;
1289  default:
1290  break;
1291  }
1292  }
1293 
1294  StringList MUCRoom::handleDiscoNodeFeatures( const JID& /*from*/, const std::string& /*node*/ )
1295  {
1296  return StringList();
1297  }
1298 
1299  Disco::IdentityList MUCRoom::handleDiscoNodeIdentities( const JID& /*from*/,
1300  const std::string& /*node*/ )
1301  {
1302  return Disco::IdentityList();
1303  }
1304 
1305  Disco::ItemList MUCRoom::handleDiscoNodeItems( const JID& /*from*/, const JID& /*to*/,
1306  const std::string& node )
1307  {
1308  Disco::ItemList l;
1309  if( node == XMLNS_MUC_ROOMS && m_publish )
1310  {
1311  l.push_back( new Disco::Item( m_nick.bareJID(), EmptyString,
1312  m_publishNick ? m_nick.resource() : EmptyString ) );
1313  }
1314  return l;
1315  }
1316 
1317 }