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();
state()73 virtual XmppChatroomState state() { return chatroom_state_; }
74 virtual XmppReturnStatus SendMessage(const XmlElement& message);
75
76 // XmppModule
IqResponse(XmppIqCookie cookie,const XmlElement * pelStanza)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:
~XmppChatroomMemberImpl()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 *
Create()152 XmppChatroomModule::Create() {
153 return new XmppChatroomModuleImpl();
154 }
155
XmppChatroomModuleImpl()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
~XmppChatroomModuleImpl()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
HandleStanza(const XmlElement * stanza)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
set_chatroom_handler(XmppChatroomHandler * handler)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*
chatroom_handler()206 XmppChatroomModuleImpl::chatroom_handler() {
207 return chatroom_handler_;
208 }
209
210 XmppReturnStatus
set_chatroom_jid(const Jid & chatroom_jid)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&
chatroom_jid() const225 XmppChatroomModuleImpl::chatroom_jid() const {
226 return chatroom_jid_;
227 }
228
229 XmppReturnStatus
set_nickname(const std::string & nickname)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&
nickname() const239 XmppChatroomModuleImpl::nickname() const {
240 return nickname_;
241 }
242
243 const Jid
member_jid() const244 XmppChatroomModuleImpl::member_jid() const {
245 return Jid(chatroom_jid_.node(), chatroom_jid_.domain(), nickname_);
246 }
247
248
249 bool
CheckEnterChatroomStateOk()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
GetAttrValueFor(XmppPresenceConnectionStatus connection_status)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
RequestEnterChatroom(const std::string & password,const std::string & client_version,const std::string & locale)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
RequestExitChatroom()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
RequestConnectionStatusChange(XmppPresenceConnectionStatus connection_status)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
GetChatroomMemberCount()370 XmppChatroomModuleImpl::GetChatroomMemberCount() {
371 return chatroom_jid_members_.size();
372 }
373
374 XmppReturnStatus
CreateMemberEnumerator(XmppChatroomMemberEnumerator ** enumerator)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
subject()381 XmppChatroomModuleImpl::subject() {
382 return ""; //NYI
383 }
384
385 XmppReturnStatus
SendMessage(const XmlElement & message)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
FireEnteredStatus(const XmlElement * presence,XmppChatroomEnteredStatus status)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
FireExitStatus(XmppChatroomExitedStatus status)460 XmppChatroomModuleImpl::FireExitStatus(XmppChatroomExitedStatus status) {
461 if (chatroom_handler_)
462 chatroom_handler_->ChatroomExitedStatus(this, status);
463 }
464
465 void
FireMessageReceived(const XmlElement & message)466 XmppChatroomModuleImpl::FireMessageReceived(const XmlElement& message) {
467 if (chatroom_handler_)
468 chatroom_handler_->MessageReceived(this, message);
469 }
470
471 void
FireMemberEntered(const XmppChatroomMember * entered_member)472 XmppChatroomModuleImpl::FireMemberEntered(const XmppChatroomMember* entered_member) {
473 if (chatroom_handler_)
474 chatroom_handler_->MemberEntered(this, entered_member);
475 }
476
477 void
FireMemberChanged(const XmppChatroomMember * changed_member)478 XmppChatroomModuleImpl::FireMemberChanged(
479 const XmppChatroomMember* changed_member) {
480 if (chatroom_handler_)
481 chatroom_handler_->MemberChanged(this, changed_member);
482 }
483
484 void
FireMemberExited(const XmppChatroomMember * exited_member)485 XmppChatroomModuleImpl::FireMemberExited(const XmppChatroomMember* exited_member) {
486 if (chatroom_handler_)
487 chatroom_handler_->MemberExited(this, exited_member);
488 }
489
490
491 XmppReturnStatus
ServerChangedOtherPresence(const XmlElement & presence_element)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
ClientChangeMyPresence(XmppChatroomState new_state)527 XmppChatroomModuleImpl::ClientChangeMyPresence(XmppChatroomState new_state) {
528 return ChangePresence(new_state, NULL, false);
529 }
530
531 XmppReturnStatus
ServerChangeMyPresence(const XmlElement & presence)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
ChangePresence(XmppChatroomState new_state,const XmlElement * presence,bool isServer)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
GetEnterFailureFromXml(const XmlElement * presence)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
GetExitFailureFromXml(const XmlElement * presence)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
SetPresence(const XmppPresence * presence)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
member_jid() const666 XmppChatroomMemberImpl::member_jid() const {
667 return presence_->jid();
668 }
669
670 const Jid
full_jid() const671 XmppChatroomMemberImpl::full_jid() const {
672 return Jid("");
673 }
674
675 const std::string
name() const676 XmppChatroomMemberImpl::name() const {
677 return member_jid().resource();
678 }
679
680 const XmppPresence*
presence() const681 XmppChatroomMemberImpl::presence() const {
682 return presence_.get();
683 }
684
685
686 // XmppChatroomMemberEnumeratorImpl --------------------------------------
XmppChatroomMemberEnumeratorImpl(XmppChatroomModuleImpl::JidMemberMap * map,int * map_version)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*
current()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
Prev()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
Next()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
IsValid()739 XmppChatroomMemberEnumeratorImpl::IsValid() {
740 return map_version_created_ == *map_version_;
741 }
742
743 bool
IsBeforeBeginning()744 XmppChatroomMemberEnumeratorImpl::IsBeforeBeginning() {
745 return before_beginning_;
746 }
747
748 bool
IsAfterEnd()749 XmppChatroomMemberEnumeratorImpl::IsAfterEnd() {
750 return (iterator_ == map_->end());
751 }
752
753
754
755 } // namespace buzz
756