1 /* 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <algorithm> 12 #include <iostream> 13 #include <map> 14 #include <sstream> 15 #include <string> 16 #include <vector> 17 #include "webrtc/libjingle/xmpp/chatroommodule.h" 18 #include "webrtc/libjingle/xmpp/constants.h" 19 #include "webrtc/libjingle/xmpp/moduleimpl.h" 20 #include "webrtc/base/arraysize.h" 21 #include "webrtc/base/common.h" 22 23 namespace buzz { 24 25 // forward declarations 26 class XmppChatroomImpl; 27 class XmppChatroomMemberImpl; 28 29 //! Module that encapsulates multiple chatrooms. 30 //! Each chatroom is represented by an XmppChatroomImpl instance 31 class XmppChatroomModuleImpl : public XmppChatroomModule, 32 public XmppModuleImpl, public XmppIqHandler { 33 public: 34 IMPLEMENT_XMPPMODULE 35 36 // Creates a chatroom with specified Jid 37 XmppChatroomModuleImpl(); 38 ~XmppChatroomModuleImpl(); 39 40 // XmppChatroomModule 41 virtual XmppReturnStatus set_chatroom_handler(XmppChatroomHandler* handler); 42 virtual XmppChatroomHandler* chatroom_handler(); 43 virtual XmppReturnStatus set_chatroom_jid(const Jid& chatroom_jid); 44 virtual const Jid& chatroom_jid() const; 45 virtual XmppReturnStatus set_nickname(const std::string& nickname); 46 virtual const std::string& nickname() const; 47 virtual const Jid member_jid() const; 48 virtual XmppReturnStatus RequestEnterChatroom(const std::string& password, 49 const std::string& client_version, 50 const std::string& locale); 51 virtual XmppReturnStatus RequestExitChatroom(); 52 virtual XmppReturnStatus RequestConnectionStatusChange( 53 XmppPresenceConnectionStatus connection_status); 54 virtual size_t GetChatroomMemberCount(); 55 virtual XmppReturnStatus CreateMemberEnumerator(XmppChatroomMemberEnumerator** enumerator); 56 virtual const std::string subject(); 57 virtual XmppChatroomState state() { return chatroom_state_; } 58 virtual XmppReturnStatus SendMessage(const XmlElement& message); 59 60 // XmppModule 61 virtual void IqResponse(XmppIqCookie cookie, const XmlElement * pelStanza) {RTC_UNUSED2(cookie, pelStanza);} 62 virtual bool HandleStanza(const XmlElement *); 63 64 private: 65 friend class XmppChatroomMemberEnumeratorImpl; 66 67 XmppReturnStatus ServerChangeMyPresence(const XmlElement& presence); 68 XmppReturnStatus ClientChangeMyPresence(XmppChatroomState new_state); 69 XmppReturnStatus ChangePresence(XmppChatroomState new_state, const XmlElement* presence, bool isServer); 70 XmppReturnStatus ServerChangedOtherPresence(const XmlElement& presence_element); 71 XmppChatroomEnteredStatus GetEnterFailureFromXml(const XmlElement* presence); 72 XmppChatroomExitedStatus GetExitFailureFromXml(const XmlElement* presence); 73 74 bool CheckEnterChatroomStateOk(); 75 76 void FireEnteredStatus(const XmlElement* presence, 77 XmppChatroomEnteredStatus status); 78 void FireExitStatus(XmppChatroomExitedStatus status); 79 void FireMessageReceived(const XmlElement& message); 80 void FireMemberEntered(const XmppChatroomMember* entered_member); 81 void FireMemberChanged(const XmppChatroomMember* changed_member); 82 void FireMemberExited(const XmppChatroomMember* exited_member); 83 84 85 typedef std::map<Jid, XmppChatroomMemberImpl*> JidMemberMap; 86 87 XmppChatroomHandler* chatroom_handler_; 88 Jid chatroom_jid_; 89 std::string nickname_; 90 XmppChatroomState chatroom_state_; 91 JidMemberMap chatroom_jid_members_; 92 int chatroom_jid_members_version_; 93 }; 94 95 96 class XmppChatroomMemberImpl : public XmppChatroomMember { 97 public: 98 ~XmppChatroomMemberImpl() {} 99 XmppReturnStatus SetPresence(const XmppPresence* presence); 100 101 // XmppChatroomMember 102 const Jid member_jid() const; 103 const Jid full_jid() const; 104 const std::string name() const; 105 const XmppPresence* presence() const; 106 107 private: 108 rtc::scoped_ptr<XmppPresence> presence_; 109 }; 110 111 class XmppChatroomMemberEnumeratorImpl : 112 public XmppChatroomMemberEnumerator { 113 public: 114 XmppChatroomMemberEnumeratorImpl(XmppChatroomModuleImpl::JidMemberMap* chatroom_jid_members, 115 int* map_version); 116 117 // XmppChatroomMemberEnumerator 118 virtual XmppChatroomMember* current(); 119 virtual bool Next(); 120 virtual bool Prev(); 121 virtual bool IsValid(); 122 virtual bool IsBeforeBeginning(); 123 virtual bool IsAfterEnd(); 124 125 private: 126 XmppChatroomModuleImpl::JidMemberMap* map_; 127 int map_version_created_; 128 int* map_version_; 129 XmppChatroomModuleImpl::JidMemberMap::iterator iterator_; 130 bool before_beginning_; 131 }; 132 133 134 // XmppChatroomModuleImpl ------------------------------------------------ 135 XmppChatroomModule * 136 XmppChatroomModule::Create() { 137 return new XmppChatroomModuleImpl(); 138 } 139 140 XmppChatroomModuleImpl::XmppChatroomModuleImpl() : 141 chatroom_handler_(NULL), 142 chatroom_jid_(STR_EMPTY), 143 chatroom_state_(XMPP_CHATROOM_STATE_NOT_IN_ROOM), 144 chatroom_jid_members_version_(0) { 145 } 146 147 XmppChatroomModuleImpl::~XmppChatroomModuleImpl() { 148 JidMemberMap::iterator iterator = chatroom_jid_members_.begin(); 149 while (iterator != chatroom_jid_members_.end()) { 150 delete iterator->second; 151 iterator++; 152 } 153 } 154 155 156 bool 157 XmppChatroomModuleImpl::HandleStanza(const XmlElement* stanza) { 158 ASSERT(engine() != NULL); 159 160 // we handle stanzas that are for one of our chatrooms 161 Jid from_jid = Jid(stanza->Attr(QN_FROM)); 162 // see if it's one of our chatrooms 163 if (chatroom_jid_ != from_jid.BareJid()) { 164 return false; // not one of our chatrooms 165 } else { 166 // handle presence stanza 167 if (stanza->Name() == QN_PRESENCE) { 168 if (from_jid == member_jid()) { 169 ServerChangeMyPresence(*stanza); 170 } else { 171 ServerChangedOtherPresence(*stanza); 172 } 173 } else if (stanza->Name() == QN_MESSAGE) { 174 FireMessageReceived(*stanza); 175 } 176 return true; 177 } 178 } 179 180 181 XmppReturnStatus 182 XmppChatroomModuleImpl::set_chatroom_handler(XmppChatroomHandler* handler) { 183 // Calling with NULL removes the handler. 184 chatroom_handler_ = handler; 185 return XMPP_RETURN_OK; 186 } 187 188 189 XmppChatroomHandler* 190 XmppChatroomModuleImpl::chatroom_handler() { 191 return chatroom_handler_; 192 } 193 194 XmppReturnStatus 195 XmppChatroomModuleImpl::set_chatroom_jid(const Jid& chatroom_jid) { 196 if (chatroom_state_ != XMPP_CHATROOM_STATE_NOT_IN_ROOM) { 197 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code? 198 } 199 if (chatroom_jid != chatroom_jid.BareJid()) { 200 // chatroom_jid must be a bare jid 201 return XMPP_RETURN_BADARGUMENT; 202 } 203 204 chatroom_jid_ = chatroom_jid; 205 return XMPP_RETURN_OK; 206 } 207 208 const Jid& 209 XmppChatroomModuleImpl::chatroom_jid() const { 210 return chatroom_jid_; 211 } 212 213 XmppReturnStatus 214 XmppChatroomModuleImpl::set_nickname(const std::string& nickname) { 215 if (chatroom_state_ != XMPP_CHATROOM_STATE_NOT_IN_ROOM) { 216 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code? 217 } 218 nickname_ = nickname; 219 return XMPP_RETURN_OK; 220 } 221 222 const std::string& 223 XmppChatroomModuleImpl::nickname() const { 224 return nickname_; 225 } 226 227 const Jid 228 XmppChatroomModuleImpl::member_jid() const { 229 return Jid(chatroom_jid_.node(), chatroom_jid_.domain(), nickname_); 230 } 231 232 233 bool 234 XmppChatroomModuleImpl::CheckEnterChatroomStateOk() { 235 if (chatroom_jid_.IsValid() == false) { 236 ASSERT(0); 237 return false; 238 } 239 if (nickname_ == STR_EMPTY) { 240 ASSERT(0); 241 return false; 242 } 243 return true; 244 } 245 246 std::string GetAttrValueFor(XmppPresenceConnectionStatus connection_status) { 247 switch (connection_status) { 248 default: 249 case XMPP_CONNECTION_STATUS_UNKNOWN: 250 return ""; 251 case XMPP_CONNECTION_STATUS_CONNECTING: 252 return STR_PSTN_CONFERENCE_STATUS_CONNECTING; 253 case XMPP_CONNECTION_STATUS_CONNECTED: 254 return STR_PSTN_CONFERENCE_STATUS_CONNECTED; 255 } 256 } 257 258 XmppReturnStatus 259 XmppChatroomModuleImpl::RequestEnterChatroom( 260 const std::string& password, 261 const std::string& client_version, 262 const std::string& locale) { 263 RTC_UNUSED(password); 264 if (!engine()) 265 return XMPP_RETURN_BADSTATE; 266 267 if (chatroom_state_ != XMPP_CHATROOM_STATE_NOT_IN_ROOM) 268 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code? 269 270 if (CheckEnterChatroomStateOk() == false) { 271 return XMPP_RETURN_BADSTATE; 272 } 273 274 // entering a chatroom is a presence request to the server 275 XmlElement element(QN_PRESENCE); 276 element.AddAttr(QN_TO, member_jid().Str()); 277 278 XmlElement* muc_x = new XmlElement(QN_MUC_X); 279 element.AddElement(muc_x); 280 281 if (!client_version.empty()) { 282 XmlElement* client_version_element = new XmlElement(QN_CLIENT_VERSION, 283 false); 284 client_version_element->SetBodyText(client_version); 285 muc_x->AddElement(client_version_element); 286 } 287 288 if (!locale.empty()) { 289 XmlElement* locale_element = new XmlElement(QN_LOCALE, false); 290 291 locale_element->SetBodyText(locale); 292 muc_x->AddElement(locale_element); 293 } 294 295 XmppReturnStatus status = engine()->SendStanza(&element); 296 if (status == XMPP_RETURN_OK) { 297 return ClientChangeMyPresence(XMPP_CHATROOM_STATE_REQUESTED_ENTER); 298 } 299 return status; 300 } 301 302 XmppReturnStatus 303 XmppChatroomModuleImpl::RequestExitChatroom() { 304 if (!engine()) 305 return XMPP_RETURN_BADSTATE; 306 307 // exiting a chatroom is a presence request to the server 308 XmlElement element(QN_PRESENCE); 309 element.AddAttr(QN_TO, member_jid().Str()); 310 element.AddAttr(QN_TYPE, "unavailable"); 311 XmppReturnStatus status = engine()->SendStanza(&element); 312 if (status == XMPP_RETURN_OK && 313 chatroom_state_ == XMPP_CHATROOM_STATE_IN_ROOM) { 314 return ClientChangeMyPresence(XMPP_CHATROOM_STATE_REQUESTED_EXIT); 315 } 316 return status; 317 } 318 319 XmppReturnStatus 320 XmppChatroomModuleImpl::RequestConnectionStatusChange( 321 XmppPresenceConnectionStatus connection_status) { 322 if (!engine()) 323 return XMPP_RETURN_BADSTATE; 324 325 if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) { 326 // $TODO - this isn't a bad state, it's a bad call, diff error code? 327 return XMPP_RETURN_BADSTATE; 328 } 329 330 if (CheckEnterChatroomStateOk() == false) { 331 return XMPP_RETURN_BADSTATE; 332 } 333 334 // entering a chatroom is a presence request to the server 335 XmlElement element(QN_PRESENCE); 336 element.AddAttr(QN_TO, member_jid().Str()); 337 element.AddElement(new XmlElement(QN_MUC_X)); 338 if (connection_status != XMPP_CONNECTION_STATUS_UNKNOWN) { 339 XmlElement* con_status_element = 340 new XmlElement(QN_GOOGLE_PSTN_CONFERENCE_STATUS); 341 con_status_element->AddAttr(QN_STATUS, GetAttrValueFor(connection_status)); 342 element.AddElement(con_status_element); 343 } 344 XmppReturnStatus status = engine()->SendStanza(&element); 345 346 return status; 347 } 348 349 size_t 350 XmppChatroomModuleImpl::GetChatroomMemberCount() { 351 return chatroom_jid_members_.size(); 352 } 353 354 XmppReturnStatus 355 XmppChatroomModuleImpl::CreateMemberEnumerator(XmppChatroomMemberEnumerator** enumerator) { 356 *enumerator = new XmppChatroomMemberEnumeratorImpl(&chatroom_jid_members_, &chatroom_jid_members_version_); 357 return XMPP_RETURN_OK; 358 } 359 360 const std::string 361 XmppChatroomModuleImpl::subject() { 362 return ""; //NYI 363 } 364 365 XmppReturnStatus 366 XmppChatroomModuleImpl::SendMessage(const XmlElement& message) { 367 XmppReturnStatus xmpp_status = XMPP_RETURN_OK; 368 369 // can only send a message if we're in the room 370 if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) { 371 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code? 372 } 373 374 if (message.Name() != QN_MESSAGE) { 375 IFR(XMPP_RETURN_BADARGUMENT); 376 } 377 378 const std::string& type = message.Attr(QN_TYPE); 379 if (type != "groupchat") { 380 IFR(XMPP_RETURN_BADARGUMENT); 381 } 382 383 if (message.HasAttr(QN_FROM)) { 384 IFR(XMPP_RETURN_BADARGUMENT); 385 } 386 387 if (message.Attr(QN_TO) != chatroom_jid_.Str()) { 388 IFR(XMPP_RETURN_BADARGUMENT); 389 } 390 391 IFR(engine()->SendStanza(&message)); 392 393 return xmpp_status; 394 } 395 396 enum TransitionType { 397 TRANSITION_TYPE_NONE = 0, 398 TRANSITION_TYPE_ENTER_SUCCESS = 1, 399 TRANSITION_TYPE_ENTER_FAILURE = 2, 400 TRANSITION_TYPE_EXIT_VOLUNTARILY = 3, 401 TRANSITION_TYPE_EXIT_INVOLUNTARILY = 4, 402 }; 403 404 struct StateTransitionDescription { 405 XmppChatroomState old_state; 406 XmppChatroomState new_state; 407 bool is_valid_server_transition; 408 bool is_valid_client_transition; 409 TransitionType transition_type; 410 }; 411 412 StateTransitionDescription Transitions[] = { 413 { XMPP_CHATROOM_STATE_NOT_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, true, TRANSITION_TYPE_NONE, }, 414 { XMPP_CHATROOM_STATE_NOT_IN_ROOM, XMPP_CHATROOM_STATE_IN_ROOM, false, false, TRANSITION_TYPE_ENTER_SUCCESS, }, 415 { XMPP_CHATROOM_STATE_NOT_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_EXIT, false, false, TRANSITION_TYPE_NONE, }, 416 { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_NOT_IN_ROOM, true, false, TRANSITION_TYPE_ENTER_FAILURE, }, 417 { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_IN_ROOM, true, false, TRANSITION_TYPE_ENTER_SUCCESS, }, 418 { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_REQUESTED_EXIT, false, false, TRANSITION_TYPE_NONE, }, 419 { XMPP_CHATROOM_STATE_IN_ROOM, XMPP_CHATROOM_STATE_NOT_IN_ROOM, true, false, TRANSITION_TYPE_EXIT_INVOLUNTARILY, }, 420 { XMPP_CHATROOM_STATE_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, false, TRANSITION_TYPE_NONE, }, 421 { XMPP_CHATROOM_STATE_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_EXIT, false, true, TRANSITION_TYPE_NONE, }, 422 { XMPP_CHATROOM_STATE_REQUESTED_EXIT, XMPP_CHATROOM_STATE_NOT_IN_ROOM, true, false, TRANSITION_TYPE_EXIT_VOLUNTARILY, }, 423 { XMPP_CHATROOM_STATE_REQUESTED_EXIT, XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, false, TRANSITION_TYPE_NONE, }, 424 { XMPP_CHATROOM_STATE_REQUESTED_EXIT, XMPP_CHATROOM_STATE_IN_ROOM, false, false, TRANSITION_TYPE_NONE, }, 425 }; 426 427 428 429 void 430 XmppChatroomModuleImpl::FireEnteredStatus(const XmlElement* presence, 431 XmppChatroomEnteredStatus status) { 432 if (chatroom_handler_) { 433 rtc::scoped_ptr<XmppPresence> xmpp_presence(XmppPresence::Create()); 434 xmpp_presence->set_raw_xml(presence); 435 chatroom_handler_->ChatroomEnteredStatus(this, xmpp_presence.get(), status); 436 } 437 } 438 439 void 440 XmppChatroomModuleImpl::FireExitStatus(XmppChatroomExitedStatus status) { 441 if (chatroom_handler_) 442 chatroom_handler_->ChatroomExitedStatus(this, status); 443 } 444 445 void 446 XmppChatroomModuleImpl::FireMessageReceived(const XmlElement& message) { 447 if (chatroom_handler_) 448 chatroom_handler_->MessageReceived(this, message); 449 } 450 451 void 452 XmppChatroomModuleImpl::FireMemberEntered(const XmppChatroomMember* entered_member) { 453 if (chatroom_handler_) 454 chatroom_handler_->MemberEntered(this, entered_member); 455 } 456 457 void 458 XmppChatroomModuleImpl::FireMemberChanged( 459 const XmppChatroomMember* changed_member) { 460 if (chatroom_handler_) 461 chatroom_handler_->MemberChanged(this, changed_member); 462 } 463 464 void 465 XmppChatroomModuleImpl::FireMemberExited(const XmppChatroomMember* exited_member) { 466 if (chatroom_handler_) 467 chatroom_handler_->MemberExited(this, exited_member); 468 } 469 470 471 XmppReturnStatus 472 XmppChatroomModuleImpl::ServerChangedOtherPresence(const XmlElement& 473 presence_element) { 474 XmppReturnStatus xmpp_status = XMPP_RETURN_OK; 475 rtc::scoped_ptr<XmppPresence> presence(XmppPresence::Create()); 476 IFR(presence->set_raw_xml(&presence_element)); 477 478 JidMemberMap::iterator pos = chatroom_jid_members_.find(presence->jid()); 479 480 if (pos == chatroom_jid_members_.end()) { 481 if (presence->available() == XMPP_PRESENCE_AVAILABLE) { 482 XmppChatroomMemberImpl* member = new XmppChatroomMemberImpl(); 483 member->SetPresence(presence.get()); 484 chatroom_jid_members_.insert(std::make_pair(member->member_jid(), member)); 485 chatroom_jid_members_version_++; 486 FireMemberEntered(member); 487 } 488 } else { 489 XmppChatroomMemberImpl* member = pos->second; 490 if (presence->available() == XMPP_PRESENCE_AVAILABLE) { 491 member->SetPresence(presence.get()); 492 chatroom_jid_members_version_++; 493 FireMemberChanged(member); 494 } 495 else if (presence->available() == XMPP_PRESENCE_UNAVAILABLE) { 496 member->SetPresence(presence.get()); 497 chatroom_jid_members_.erase(pos); 498 chatroom_jid_members_version_++; 499 FireMemberExited(member); 500 delete member; 501 } 502 } 503 504 return xmpp_status; 505 } 506 507 XmppReturnStatus 508 XmppChatroomModuleImpl::ClientChangeMyPresence(XmppChatroomState new_state) { 509 return ChangePresence(new_state, NULL, false); 510 } 511 512 XmppReturnStatus 513 XmppChatroomModuleImpl::ServerChangeMyPresence(const XmlElement& presence) { 514 XmppChatroomState new_state; 515 516 if (presence.HasAttr(QN_TYPE) == false) { 517 new_state = XMPP_CHATROOM_STATE_IN_ROOM; 518 } else { 519 new_state = XMPP_CHATROOM_STATE_NOT_IN_ROOM; 520 } 521 return ChangePresence(new_state, &presence, true); 522 523 } 524 525 XmppReturnStatus 526 XmppChatroomModuleImpl::ChangePresence(XmppChatroomState new_state, 527 const XmlElement* presence, 528 bool isServer) { 529 RTC_UNUSED(presence); 530 531 XmppChatroomState old_state = chatroom_state_; 532 533 // do nothing if state hasn't changed 534 if (old_state == new_state) 535 return XMPP_RETURN_OK; 536 537 // find the right transition description 538 StateTransitionDescription* transition_desc = NULL; 539 for (size_t i = 0; i < arraysize(Transitions); i++) { 540 if (Transitions[i].old_state == old_state && 541 Transitions[i].new_state == new_state) { 542 transition_desc = &Transitions[i]; 543 break; 544 } 545 } 546 547 if (transition_desc == NULL) { 548 ASSERT(0); 549 return XMPP_RETURN_BADSTATE; 550 } 551 552 // we assert for any invalid transition states, and we'll 553 if (isServer) { 554 // $TODO send original stanza back to server and log an error? 555 // Disable the assert because of b/6133072 556 // ASSERT(transition_desc->is_valid_server_transition); 557 if (!transition_desc->is_valid_server_transition) { 558 return XMPP_RETURN_BADSTATE; 559 } 560 } else { 561 if (transition_desc->is_valid_client_transition == false) { 562 ASSERT(0); 563 return XMPP_RETURN_BADARGUMENT; 564 } 565 } 566 567 // set the new state and then fire any notifications to the handler 568 chatroom_state_ = new_state; 569 570 switch (transition_desc->transition_type) { 571 case TRANSITION_TYPE_ENTER_SUCCESS: 572 FireEnteredStatus(presence, XMPP_CHATROOM_ENTERED_SUCCESS); 573 break; 574 case TRANSITION_TYPE_ENTER_FAILURE: 575 FireEnteredStatus(presence, GetEnterFailureFromXml(presence)); 576 break; 577 case TRANSITION_TYPE_EXIT_INVOLUNTARILY: 578 FireExitStatus(GetExitFailureFromXml(presence)); 579 break; 580 case TRANSITION_TYPE_EXIT_VOLUNTARILY: 581 FireExitStatus(XMPP_CHATROOM_EXITED_REQUESTED); 582 break; 583 case TRANSITION_TYPE_NONE: 584 break; 585 } 586 587 return XMPP_RETURN_OK; 588 } 589 590 XmppChatroomEnteredStatus 591 XmppChatroomModuleImpl::GetEnterFailureFromXml(const XmlElement* presence) { 592 XmppChatroomEnteredStatus status = XMPP_CHATROOM_ENTERED_FAILURE_UNSPECIFIED; 593 const XmlElement* error = presence->FirstNamed(QN_ERROR); 594 if (error != NULL && error->HasAttr(QN_CODE)) { 595 int code = atoi(error->Attr(QN_CODE).c_str()); 596 switch (code) { 597 case 401: status = XMPP_CHATROOM_ENTERED_FAILURE_PASSWORD_REQUIRED; break; 598 case 403: { 599 status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BANNED; 600 if (error->FirstNamed(QN_GOOGLE_SESSION_BLOCKED)) { 601 status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BLOCKED; 602 } else if (error->FirstNamed(QN_GOOGLE_SESSION_BLOCKING)) { 603 status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BLOCKING; 604 } 605 break; 606 } 607 case 405: status = XMPP_CHATROOM_ENTERED_FAILURE_ROOM_LOCKED; break; 608 case 406: status = XMPP_CHATROOM_ENTERED_FAILURE_OUTDATED_CLIENT; break; 609 case 407: status = XMPP_CHATROOM_ENTERED_FAILURE_NOT_A_MEMBER; break; 610 case 409: status = XMPP_CHATROOM_ENTERED_FAILURE_NICKNAME_CONFLICT; break; 611 // http://xmpp.org/extensions/xep-0045.html#enter-maxusers 612 case 503: status = XMPP_CHATROOM_ENTERED_FAILURE_MAX_USERS; break; 613 } 614 } 615 return status; 616 } 617 618 XmppChatroomExitedStatus 619 XmppChatroomModuleImpl::GetExitFailureFromXml(const XmlElement* presence) { 620 XmppChatroomExitedStatus status = XMPP_CHATROOM_EXITED_UNSPECIFIED; 621 const XmlElement* muc_user = presence->FirstNamed(QN_MUC_USER_X); 622 if (muc_user != NULL) { 623 const XmlElement* user_status = muc_user->FirstNamed(QN_MUC_USER_STATUS); 624 if (user_status != NULL && user_status->HasAttr(QN_CODE)) { 625 int code = atoi(user_status->Attr(QN_CODE).c_str()); 626 switch (code) { 627 case 307: status = XMPP_CHATROOM_EXITED_KICKED; break; 628 case 322: status = XMPP_CHATROOM_EXITED_NOT_A_MEMBER; break; 629 case 332: status = XMPP_CHATROOM_EXITED_SYSTEM_SHUTDOWN; break; 630 } 631 } 632 } 633 return status; 634 } 635 636 XmppReturnStatus 637 XmppChatroomMemberImpl::SetPresence(const XmppPresence* presence) { 638 ASSERT(presence != NULL); 639 640 // copy presence 641 presence_.reset(XmppPresence::Create()); 642 presence_->set_raw_xml(presence->raw_xml()); 643 return XMPP_RETURN_OK; 644 } 645 646 const Jid 647 XmppChatroomMemberImpl::member_jid() const { 648 return presence_->jid(); 649 } 650 651 const Jid 652 XmppChatroomMemberImpl::full_jid() const { 653 return Jid(""); 654 } 655 656 const std::string 657 XmppChatroomMemberImpl::name() const { 658 return member_jid().resource(); 659 } 660 661 const XmppPresence* 662 XmppChatroomMemberImpl::presence() const { 663 return presence_.get(); 664 } 665 666 667 // XmppChatroomMemberEnumeratorImpl -------------------------------------- 668 XmppChatroomMemberEnumeratorImpl::XmppChatroomMemberEnumeratorImpl( 669 XmppChatroomModuleImpl::JidMemberMap* map, int* map_version) { 670 map_ = map; 671 map_version_ = map_version; 672 map_version_created_ = *map_version_; 673 iterator_ = map->begin(); 674 before_beginning_ = true; 675 } 676 677 XmppChatroomMember* 678 XmppChatroomMemberEnumeratorImpl::current() { 679 if (IsValid() == false) { 680 return NULL; 681 } else if (IsBeforeBeginning() || IsAfterEnd()) { 682 return NULL; 683 } else { 684 return iterator_->second; 685 } 686 } 687 688 bool 689 XmppChatroomMemberEnumeratorImpl::Prev() { 690 if (IsValid() == false) { 691 return false; 692 } else if (IsBeforeBeginning()) { 693 return false; 694 } else if (iterator_ == map_->begin()) { 695 before_beginning_ = true; 696 return false; 697 } else { 698 iterator_--; 699 return current() != NULL; 700 } 701 } 702 703 bool 704 XmppChatroomMemberEnumeratorImpl::Next() { 705 if (IsValid() == false) { 706 return false; 707 } else if (IsBeforeBeginning()) { 708 before_beginning_ = false; 709 iterator_ = map_->begin(); 710 return current() != NULL; 711 } else if (IsAfterEnd()) { 712 return false; 713 } else { 714 iterator_++; 715 return current() != NULL; 716 } 717 } 718 719 bool 720 XmppChatroomMemberEnumeratorImpl::IsValid() { 721 return map_version_created_ == *map_version_; 722 } 723 724 bool 725 XmppChatroomMemberEnumeratorImpl::IsBeforeBeginning() { 726 return before_beginning_; 727 } 728 729 bool 730 XmppChatroomMemberEnumeratorImpl::IsAfterEnd() { 731 return (iterator_ == map_->end()); 732 } 733 734 735 736 } // namespace buzz 737