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 // currently, can't leave a room unless you've entered 324 // no way to cancel a pending enter call - is that bad? 325 if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) 326 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code? 327 328 // exiting a chatroom is a presence request to the server 329 XmlElement element(QN_PRESENCE); 330 element.AddAttr(QN_TO, member_jid().Str()); 331 element.AddAttr(QN_TYPE, "unavailable"); 332 XmppReturnStatus status = engine()->SendStanza(&element); 333 if (status == XMPP_RETURN_OK) { 334 return ClientChangeMyPresence(XMPP_CHATROOM_STATE_REQUESTED_EXIT); 335 } 336 return status; 337 } 338 339 XmppReturnStatus 340 XmppChatroomModuleImpl::RequestConnectionStatusChange( 341 XmppPresenceConnectionStatus connection_status) { 342 if (!engine()) 343 return XMPP_RETURN_BADSTATE; 344 345 if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) { 346 // $TODO - this isn't a bad state, it's a bad call, diff error code? 347 return XMPP_RETURN_BADSTATE; 348 } 349 350 if (CheckEnterChatroomStateOk() == false) { 351 return XMPP_RETURN_BADSTATE; 352 } 353 354 // entering a chatroom is a presence request to the server 355 XmlElement element(QN_PRESENCE); 356 element.AddAttr(QN_TO, member_jid().Str()); 357 element.AddElement(new XmlElement(QN_MUC_X)); 358 if (connection_status != XMPP_CONNECTION_STATUS_UNKNOWN) { 359 XmlElement* con_status_element = 360 new XmlElement(QN_GOOGLE_PSTN_CONFERENCE_STATUS); 361 con_status_element->AddAttr(QN_STATUS, GetAttrValueFor(connection_status)); 362 element.AddElement(con_status_element); 363 } 364 XmppReturnStatus status = engine()->SendStanza(&element); 365 366 return status; 367 } 368 369 size_t 370 XmppChatroomModuleImpl::GetChatroomMemberCount() { 371 return chatroom_jid_members_.size(); 372 } 373 374 XmppReturnStatus 375 XmppChatroomModuleImpl::CreateMemberEnumerator(XmppChatroomMemberEnumerator** enumerator) { 376 *enumerator = new XmppChatroomMemberEnumeratorImpl(&chatroom_jid_members_, &chatroom_jid_members_version_); 377 return XMPP_RETURN_OK; 378 } 379 380 const std::string 381 XmppChatroomModuleImpl::subject() { 382 return ""; //NYI 383 } 384 385 XmppReturnStatus 386 XmppChatroomModuleImpl::SendMessage(const XmlElement& message) { 387 XmppReturnStatus xmpp_status = XMPP_RETURN_OK; 388 389 // can only send a message if we're in the room 390 if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) { 391 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code? 392 } 393 394 if (message.Name() != QN_MESSAGE) { 395 IFR(XMPP_RETURN_BADARGUMENT); 396 } 397 398 const std::string& type = message.Attr(QN_TYPE); 399 if (type != "groupchat") { 400 IFR(XMPP_RETURN_BADARGUMENT); 401 } 402 403 if (message.HasAttr(QN_FROM)) { 404 IFR(XMPP_RETURN_BADARGUMENT); 405 } 406 407 if (message.Attr(QN_TO) != chatroom_jid_.Str()) { 408 IFR(XMPP_RETURN_BADARGUMENT); 409 } 410 411 IFR(engine()->SendStanza(&message)); 412 413 return xmpp_status; 414 } 415 416 enum TransitionType { 417 TRANSITION_TYPE_NONE = 0, 418 TRANSITION_TYPE_ENTER_SUCCESS = 1, 419 TRANSITION_TYPE_ENTER_FAILURE = 2, 420 TRANSITION_TYPE_EXIT_VOLUNTARILY = 3, 421 TRANSITION_TYPE_EXIT_INVOLUNTARILY = 4, 422 }; 423 424 struct StateTransitionDescription { 425 XmppChatroomState old_state; 426 XmppChatroomState new_state; 427 bool is_valid_server_transition; 428 bool is_valid_client_transition; 429 TransitionType transition_type; 430 }; 431 432 StateTransitionDescription Transitions[] = { 433 { XMPP_CHATROOM_STATE_NOT_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, true, TRANSITION_TYPE_NONE, }, 434 { XMPP_CHATROOM_STATE_NOT_IN_ROOM, XMPP_CHATROOM_STATE_IN_ROOM, false, false, TRANSITION_TYPE_ENTER_SUCCESS, }, 435 { XMPP_CHATROOM_STATE_NOT_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_EXIT, false, false, TRANSITION_TYPE_NONE, }, 436 { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_NOT_IN_ROOM, true, false, TRANSITION_TYPE_ENTER_FAILURE, }, 437 { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_IN_ROOM, true, false, TRANSITION_TYPE_ENTER_SUCCESS, }, 438 { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_REQUESTED_EXIT, false, false, TRANSITION_TYPE_NONE, }, 439 { XMPP_CHATROOM_STATE_IN_ROOM, XMPP_CHATROOM_STATE_NOT_IN_ROOM, true, false, TRANSITION_TYPE_EXIT_INVOLUNTARILY, }, 440 { XMPP_CHATROOM_STATE_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, false, TRANSITION_TYPE_NONE, }, 441 { XMPP_CHATROOM_STATE_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_EXIT, false, true, TRANSITION_TYPE_NONE, }, 442 { XMPP_CHATROOM_STATE_REQUESTED_EXIT, XMPP_CHATROOM_STATE_NOT_IN_ROOM, true, false, TRANSITION_TYPE_EXIT_VOLUNTARILY, }, 443 { XMPP_CHATROOM_STATE_REQUESTED_EXIT, XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, false, TRANSITION_TYPE_NONE, }, 444 { XMPP_CHATROOM_STATE_REQUESTED_EXIT, XMPP_CHATROOM_STATE_IN_ROOM, false, false, TRANSITION_TYPE_NONE, }, 445 }; 446 447 448 449 void 450 XmppChatroomModuleImpl::FireEnteredStatus(const XmlElement* presence, 451 XmppChatroomEnteredStatus status) { 452 if (chatroom_handler_) { 453 talk_base::scoped_ptr<XmppPresence> xmpp_presence(XmppPresence::Create()); 454 xmpp_presence->set_raw_xml(presence); 455 chatroom_handler_->ChatroomEnteredStatus(this, xmpp_presence.get(), status); 456 } 457 } 458 459 void 460 XmppChatroomModuleImpl::FireExitStatus(XmppChatroomExitedStatus status) { 461 if (chatroom_handler_) 462 chatroom_handler_->ChatroomExitedStatus(this, status); 463 } 464 465 void 466 XmppChatroomModuleImpl::FireMessageReceived(const XmlElement& message) { 467 if (chatroom_handler_) 468 chatroom_handler_->MessageReceived(this, message); 469 } 470 471 void 472 XmppChatroomModuleImpl::FireMemberEntered(const XmppChatroomMember* entered_member) { 473 if (chatroom_handler_) 474 chatroom_handler_->MemberEntered(this, entered_member); 475 } 476 477 void 478 XmppChatroomModuleImpl::FireMemberChanged( 479 const XmppChatroomMember* changed_member) { 480 if (chatroom_handler_) 481 chatroom_handler_->MemberChanged(this, changed_member); 482 } 483 484 void 485 XmppChatroomModuleImpl::FireMemberExited(const XmppChatroomMember* exited_member) { 486 if (chatroom_handler_) 487 chatroom_handler_->MemberExited(this, exited_member); 488 } 489 490 491 XmppReturnStatus 492 XmppChatroomModuleImpl::ServerChangedOtherPresence(const XmlElement& 493 presence_element) { 494 XmppReturnStatus xmpp_status = XMPP_RETURN_OK; 495 talk_base::scoped_ptr<XmppPresence> presence(XmppPresence::Create()); 496 IFR(presence->set_raw_xml(&presence_element)); 497 498 JidMemberMap::iterator pos = chatroom_jid_members_.find(presence->jid()); 499 500 if (pos == chatroom_jid_members_.end()) { 501 if (presence->available() == XMPP_PRESENCE_AVAILABLE) { 502 XmppChatroomMemberImpl* member = new XmppChatroomMemberImpl(); 503 member->SetPresence(presence.get()); 504 chatroom_jid_members_.insert(std::make_pair(member->member_jid(), member)); 505 chatroom_jid_members_version_++; 506 FireMemberEntered(member); 507 } 508 } else { 509 XmppChatroomMemberImpl* member = pos->second; 510 if (presence->available() == XMPP_PRESENCE_AVAILABLE) { 511 member->SetPresence(presence.get()); 512 chatroom_jid_members_version_++; 513 FireMemberChanged(member); 514 } 515 else if (presence->available() == XMPP_PRESENCE_UNAVAILABLE) { 516 chatroom_jid_members_.erase(pos); 517 chatroom_jid_members_version_++; 518 FireMemberExited(member); 519 delete member; 520 } 521 } 522 523 return xmpp_status; 524 } 525 526 XmppReturnStatus 527 XmppChatroomModuleImpl::ClientChangeMyPresence(XmppChatroomState new_state) { 528 return ChangePresence(new_state, NULL, false); 529 } 530 531 XmppReturnStatus 532 XmppChatroomModuleImpl::ServerChangeMyPresence(const XmlElement& presence) { 533 XmppChatroomState new_state; 534 535 if (presence.HasAttr(QN_TYPE) == false) { 536 new_state = XMPP_CHATROOM_STATE_IN_ROOM; 537 } else { 538 new_state = XMPP_CHATROOM_STATE_NOT_IN_ROOM; 539 } 540 return ChangePresence(new_state, &presence, true); 541 542 } 543 544 XmppReturnStatus 545 XmppChatroomModuleImpl::ChangePresence(XmppChatroomState new_state, 546 const XmlElement* presence, 547 bool isServer) { 548 UNUSED(presence); 549 550 XmppChatroomState old_state = chatroom_state_; 551 552 // do nothing if state hasn't changed 553 if (old_state == new_state) 554 return XMPP_RETURN_OK; 555 556 // find the right transition description 557 StateTransitionDescription* transition_desc = NULL; 558 for (int i=0; i < ARRAY_SIZE(Transitions); i++) { 559 if (Transitions[i].old_state == old_state && 560 Transitions[i].new_state == new_state) { 561 transition_desc = &Transitions[i]; 562 break; 563 } 564 } 565 566 if (transition_desc == NULL) { 567 ASSERT(0); 568 return XMPP_RETURN_BADSTATE; 569 } 570 571 // we assert for any invalid transition states, and we'll 572 if (isServer) { 573 // $TODO send original stanza back to server and log an error? 574 // Disable the assert because of b/6133072 575 // ASSERT(transition_desc->is_valid_server_transition); 576 if (!transition_desc->is_valid_server_transition) { 577 return XMPP_RETURN_BADSTATE; 578 } 579 } else { 580 if (transition_desc->is_valid_client_transition == false) { 581 ASSERT(0); 582 return XMPP_RETURN_BADARGUMENT; 583 } 584 } 585 586 // set the new state and then fire any notifications to the handler 587 chatroom_state_ = new_state; 588 589 switch (transition_desc->transition_type) { 590 case TRANSITION_TYPE_ENTER_SUCCESS: 591 FireEnteredStatus(presence, XMPP_CHATROOM_ENTERED_SUCCESS); 592 break; 593 case TRANSITION_TYPE_ENTER_FAILURE: 594 FireEnteredStatus(presence, GetEnterFailureFromXml(presence)); 595 break; 596 case TRANSITION_TYPE_EXIT_INVOLUNTARILY: 597 FireExitStatus(GetExitFailureFromXml(presence)); 598 break; 599 case TRANSITION_TYPE_EXIT_VOLUNTARILY: 600 FireExitStatus(XMPP_CHATROOM_EXITED_REQUESTED); 601 break; 602 case TRANSITION_TYPE_NONE: 603 break; 604 } 605 606 return XMPP_RETURN_OK; 607 } 608 609 XmppChatroomEnteredStatus 610 XmppChatroomModuleImpl::GetEnterFailureFromXml(const XmlElement* presence) { 611 XmppChatroomEnteredStatus status = XMPP_CHATROOM_ENTERED_FAILURE_UNSPECIFIED; 612 const XmlElement* error = presence->FirstNamed(QN_ERROR); 613 if (error != NULL && error->HasAttr(QN_CODE)) { 614 int code = atoi(error->Attr(QN_CODE).c_str()); 615 switch (code) { 616 case 401: status = XMPP_CHATROOM_ENTERED_FAILURE_PASSWORD_REQUIRED; break; 617 case 403: { 618 status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BANNED; 619 if (error->FirstNamed(QN_GOOGLE_SESSION_BLOCKED)) { 620 status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BLOCKED; 621 } else if (error->FirstNamed(QN_GOOGLE_SESSION_BLOCKING)) { 622 status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BLOCKING; 623 } 624 break; 625 } 626 case 405: status = XMPP_CHATROOM_ENTERED_FAILURE_ROOM_LOCKED; break; 627 case 406: status = XMPP_CHATROOM_ENTERED_FAILURE_OUTDATED_CLIENT; break; 628 case 407: status = XMPP_CHATROOM_ENTERED_FAILURE_NOT_A_MEMBER; break; 629 case 409: status = XMPP_CHATROOM_ENTERED_FAILURE_NICKNAME_CONFLICT; break; 630 // http://xmpp.org/extensions/xep-0045.html#enter-maxusers 631 case 503: status = XMPP_CHATROOM_ENTERED_FAILURE_MAX_USERS; break; 632 } 633 } 634 return status; 635 } 636 637 XmppChatroomExitedStatus 638 XmppChatroomModuleImpl::GetExitFailureFromXml(const XmlElement* presence) { 639 XmppChatroomExitedStatus status = XMPP_CHATROOM_EXITED_UNSPECIFIED; 640 const XmlElement* muc_user = presence->FirstNamed(QN_MUC_USER_X); 641 if (muc_user != NULL) { 642 const XmlElement* user_status = muc_user->FirstNamed(QN_MUC_USER_STATUS); 643 if (user_status != NULL && user_status->HasAttr(QN_CODE)) { 644 int code = atoi(user_status->Attr(QN_CODE).c_str()); 645 switch (code) { 646 case 307: status = XMPP_CHATROOM_EXITED_KICKED; break; 647 case 322: status = XMPP_CHATROOM_EXITED_NOT_A_MEMBER; break; 648 case 332: status = XMPP_CHATROOM_EXITED_SYSTEM_SHUTDOWN; break; 649 } 650 } 651 } 652 return status; 653 } 654 655 XmppReturnStatus 656 XmppChatroomMemberImpl::SetPresence(const XmppPresence* presence) { 657 ASSERT(presence != NULL); 658 659 // copy presence 660 presence_.reset(XmppPresence::Create()); 661 presence_->set_raw_xml(presence->raw_xml()); 662 return XMPP_RETURN_OK; 663 } 664 665 const Jid 666 XmppChatroomMemberImpl::member_jid() const { 667 return presence_->jid(); 668 } 669 670 const Jid 671 XmppChatroomMemberImpl::full_jid() const { 672 return Jid(""); 673 } 674 675 const std::string 676 XmppChatroomMemberImpl::name() const { 677 return member_jid().resource(); 678 } 679 680 const XmppPresence* 681 XmppChatroomMemberImpl::presence() const { 682 return presence_.get(); 683 } 684 685 686 // XmppChatroomMemberEnumeratorImpl -------------------------------------- 687 XmppChatroomMemberEnumeratorImpl::XmppChatroomMemberEnumeratorImpl( 688 XmppChatroomModuleImpl::JidMemberMap* map, int* map_version) { 689 map_ = map; 690 map_version_ = map_version; 691 map_version_created_ = *map_version_; 692 iterator_ = map->begin(); 693 before_beginning_ = true; 694 } 695 696 XmppChatroomMember* 697 XmppChatroomMemberEnumeratorImpl::current() { 698 if (IsValid() == false) { 699 return NULL; 700 } else if (IsBeforeBeginning() || IsAfterEnd()) { 701 return NULL; 702 } else { 703 return iterator_->second; 704 } 705 } 706 707 bool 708 XmppChatroomMemberEnumeratorImpl::Prev() { 709 if (IsValid() == false) { 710 return false; 711 } else if (IsBeforeBeginning()) { 712 return false; 713 } else if (iterator_ == map_->begin()) { 714 before_beginning_ = true; 715 return false; 716 } else { 717 iterator_--; 718 return current() != NULL; 719 } 720 } 721 722 bool 723 XmppChatroomMemberEnumeratorImpl::Next() { 724 if (IsValid() == false) { 725 return false; 726 } else if (IsBeforeBeginning()) { 727 before_beginning_ = false; 728 iterator_ = map_->begin(); 729 return current() != NULL; 730 } else if (IsAfterEnd()) { 731 return false; 732 } else { 733 iterator_++; 734 return current() != NULL; 735 } 736 } 737 738 bool 739 XmppChatroomMemberEnumeratorImpl::IsValid() { 740 return map_version_created_ == *map_version_; 741 } 742 743 bool 744 XmppChatroomMemberEnumeratorImpl::IsBeforeBeginning() { 745 return before_beginning_; 746 } 747 748 bool 749 XmppChatroomMemberEnumeratorImpl::IsAfterEnd() { 750 return (iterator_ == map_->end()); 751 } 752 753 754 755 } // namespace buzz 756