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