Index: webrtc/libjingle/xmpp/rostermoduleimpl.cc |
diff --git a/webrtc/libjingle/xmpp/rostermoduleimpl.cc b/webrtc/libjingle/xmpp/rostermoduleimpl.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b9752896efad84b0d4970be44f3e125b4260b452 |
--- /dev/null |
+++ b/webrtc/libjingle/xmpp/rostermoduleimpl.cc |
@@ -0,0 +1,1064 @@ |
+/* |
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+ |
+#include <algorithm> |
+#include <iostream> |
+#include <map> |
+#include <sstream> |
+#include <string> |
+#include <vector> |
+#include "webrtc/libjingle/xmpp/constants.h" |
+#include "webrtc/libjingle/xmpp/rostermoduleimpl.h" |
+#include "webrtc/base/common.h" |
+#include "webrtc/base/stringencode.h" |
+ |
+namespace buzz { |
+ |
+// enum prase and persist helpers ---------------------------------------------- |
+static bool |
+StringToPresenceShow(const std::string& input, XmppPresenceShow* show) { |
+ // If this becomes a perf issue we can use a hash or a map here |
+ if (STR_SHOW_AWAY == input) |
+ *show = XMPP_PRESENCE_AWAY; |
+ else if (STR_SHOW_DND == input) |
+ *show = XMPP_PRESENCE_DND; |
+ else if (STR_SHOW_XA == input) |
+ *show = XMPP_PRESENCE_XA; |
+ else if (STR_SHOW_CHAT == input) |
+ *show = XMPP_PRESENCE_CHAT; |
+ else if (STR_EMPTY == input) |
+ *show = XMPP_PRESENCE_DEFAULT; |
+ else |
+ return false; |
+ |
+ return true; |
+} |
+ |
+static bool |
+PresenceShowToString(XmppPresenceShow show, const char** output) { |
+ switch(show) { |
+ case XMPP_PRESENCE_AWAY: |
+ *output = STR_SHOW_AWAY; |
+ return true; |
+ case XMPP_PRESENCE_CHAT: |
+ *output = STR_SHOW_CHAT; |
+ return true; |
+ case XMPP_PRESENCE_XA: |
+ *output = STR_SHOW_XA; |
+ return true; |
+ case XMPP_PRESENCE_DND: |
+ *output = STR_SHOW_DND; |
+ return true; |
+ case XMPP_PRESENCE_DEFAULT: |
+ *output = STR_EMPTY; |
+ return true; |
+ } |
+ |
+ *output = STR_EMPTY; |
+ return false; |
+} |
+ |
+static bool |
+StringToSubscriptionState(const std::string& subscription, |
+ const std::string& ask, |
+ XmppSubscriptionState* state) |
+{ |
+ if (ask == "subscribe") |
+ { |
+ if (subscription == "none") { |
+ *state = XMPP_SUBSCRIPTION_NONE_ASKED; |
+ return true; |
+ } |
+ if (subscription == "from") { |
+ *state = XMPP_SUBSCRIPTION_FROM_ASKED; |
+ return true; |
+ } |
+ } else if (ask == STR_EMPTY) |
+ { |
+ if (subscription == "none") { |
+ *state = XMPP_SUBSCRIPTION_NONE; |
+ return true; |
+ } |
+ if (subscription == "from") { |
+ *state = XMPP_SUBSCRIPTION_FROM; |
+ return true; |
+ } |
+ if (subscription == "to") { |
+ *state = XMPP_SUBSCRIPTION_TO; |
+ return true; |
+ } |
+ if (subscription == "both") { |
+ *state = XMPP_SUBSCRIPTION_BOTH; |
+ return true; |
+ } |
+ } |
+ |
+ return false; |
+} |
+ |
+static bool |
+StringToSubscriptionRequestType(const std::string& string, |
+ XmppSubscriptionRequestType* type) |
+{ |
+ if (string == "subscribe") |
+ *type = XMPP_REQUEST_SUBSCRIBE; |
+ else if (string == "unsubscribe") |
+ *type = XMPP_REQUEST_UNSUBSCRIBE; |
+ else if (string == "subscribed") |
+ *type = XMPP_REQUEST_SUBSCRIBED; |
+ else if (string == "unsubscribed") |
+ *type = XMPP_REQUEST_UNSUBSCRIBED; |
+ else |
+ return false; |
+ return true; |
+} |
+ |
+// XmppPresenceImpl class ------------------------------------------------------ |
+XmppPresence* |
+XmppPresence::Create() { |
+ return new XmppPresenceImpl(); |
+} |
+ |
+XmppPresenceImpl::XmppPresenceImpl() { |
+} |
+ |
+const Jid |
+XmppPresenceImpl::jid() const { |
+ if (!raw_xml_) |
+ return Jid(); |
+ |
+ return Jid(raw_xml_->Attr(QN_FROM)); |
+} |
+ |
+XmppPresenceAvailable |
+XmppPresenceImpl::available() const { |
+ if (!raw_xml_) |
+ return XMPP_PRESENCE_UNAVAILABLE; |
+ |
+ if (raw_xml_->Attr(QN_TYPE) == "unavailable") |
+ return XMPP_PRESENCE_UNAVAILABLE; |
+ else if (raw_xml_->Attr(QN_TYPE) == "error") |
+ return XMPP_PRESENCE_ERROR; |
+ else |
+ return XMPP_PRESENCE_AVAILABLE; |
+} |
+ |
+XmppReturnStatus |
+XmppPresenceImpl::set_available(XmppPresenceAvailable available) { |
+ if (!raw_xml_) |
+ CreateRawXmlSkeleton(); |
+ |
+ if (available == XMPP_PRESENCE_AVAILABLE) |
+ raw_xml_->ClearAttr(QN_TYPE); |
+ else if (available == XMPP_PRESENCE_UNAVAILABLE) |
+ raw_xml_->SetAttr(QN_TYPE, "unavailable"); |
+ else if (available == XMPP_PRESENCE_ERROR) |
+ raw_xml_->SetAttr(QN_TYPE, "error"); |
+ return XMPP_RETURN_OK; |
+} |
+ |
+XmppPresenceShow |
+XmppPresenceImpl::presence_show() const { |
+ if (!raw_xml_) |
+ return XMPP_PRESENCE_DEFAULT; |
+ |
+ XmppPresenceShow show = XMPP_PRESENCE_DEFAULT; |
+ StringToPresenceShow(raw_xml_->TextNamed(QN_SHOW), &show); |
+ return show; |
+} |
+ |
+XmppReturnStatus |
+XmppPresenceImpl::set_presence_show(XmppPresenceShow show) { |
+ if (!raw_xml_) |
+ CreateRawXmlSkeleton(); |
+ |
+ const char* show_string; |
+ |
+ if(!PresenceShowToString(show, &show_string)) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ raw_xml_->ClearNamedChildren(QN_SHOW); |
+ |
+ if (show!=XMPP_PRESENCE_DEFAULT) { |
+ raw_xml_->AddElement(new XmlElement(QN_SHOW)); |
+ raw_xml_->AddText(show_string, 1); |
+ } |
+ |
+ return XMPP_RETURN_OK; |
+} |
+ |
+int |
+XmppPresenceImpl::priority() const { |
+ if (!raw_xml_) |
+ return 0; |
+ |
+ int raw_priority = 0; |
+ if (!rtc::FromString(raw_xml_->TextNamed(QN_PRIORITY), &raw_priority)) |
+ raw_priority = 0; |
+ if (raw_priority < -128) |
+ raw_priority = -128; |
+ if (raw_priority > 127) |
+ raw_priority = 127; |
+ |
+ return raw_priority; |
+} |
+ |
+XmppReturnStatus |
+XmppPresenceImpl::set_priority(int priority) { |
+ if (!raw_xml_) |
+ CreateRawXmlSkeleton(); |
+ |
+ if (priority < -128 || priority > 127) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ raw_xml_->ClearNamedChildren(QN_PRIORITY); |
+ if (0 != priority) { |
+ std::string priority_string; |
+ if (rtc::ToString(priority, &priority_string)) { |
+ raw_xml_->AddElement(new XmlElement(QN_PRIORITY)); |
+ raw_xml_->AddText(priority_string, 1); |
+ } |
+ } |
+ |
+ return XMPP_RETURN_OK; |
+} |
+ |
+const std::string |
+XmppPresenceImpl::status() const { |
+ if (!raw_xml_) |
+ return STR_EMPTY; |
+ |
+ XmlElement* status_element; |
+ XmlElement* element; |
+ |
+ // Search for a status element with no xml:lang attribute on it. if we can't |
+ // find that then just return the first status element in the stanza. |
+ for (status_element = element = raw_xml_->FirstNamed(QN_STATUS); |
+ element; |
+ element = element->NextNamed(QN_STATUS)) { |
+ if (!element->HasAttr(QN_XML_LANG)) { |
+ status_element = element; |
+ break; |
+ } |
+ } |
+ |
+ if (status_element) { |
+ return status_element->BodyText(); |
+ } |
+ |
+ return STR_EMPTY; |
+} |
+ |
+XmppReturnStatus |
+XmppPresenceImpl::set_status(const std::string& status) { |
+ if (!raw_xml_) |
+ CreateRawXmlSkeleton(); |
+ |
+ raw_xml_->ClearNamedChildren(QN_STATUS); |
+ |
+ if (status != STR_EMPTY) { |
+ raw_xml_->AddElement(new XmlElement(QN_STATUS)); |
+ raw_xml_->AddText(status, 1); |
+ } |
+ |
+ return XMPP_RETURN_OK; |
+} |
+ |
+XmppPresenceConnectionStatus |
+XmppPresenceImpl::connection_status() const { |
+ if (!raw_xml_) |
+ return XMPP_CONNECTION_STATUS_UNKNOWN; |
+ |
+ XmlElement* con = raw_xml_->FirstNamed(QN_GOOGLE_PSTN_CONFERENCE_STATUS); |
+ if (con) { |
+ std::string status = con->Attr(QN_ATTR_STATUS); |
+ if (status == STR_PSTN_CONFERENCE_STATUS_CONNECTING) |
+ return XMPP_CONNECTION_STATUS_CONNECTING; |
+ else if (status == STR_PSTN_CONFERENCE_STATUS_CONNECTED) |
+ return XMPP_CONNECTION_STATUS_CONNECTED; |
+ else if (status == STR_PSTN_CONFERENCE_STATUS_JOINING) |
+ return XMPP_CONNECTION_STATUS_JOINING; |
+ else if (status == STR_PSTN_CONFERENCE_STATUS_HANGUP) |
+ return XMPP_CONNECTION_STATUS_HANGUP; |
+ } |
+ |
+ return XMPP_CONNECTION_STATUS_CONNECTED; |
+} |
+ |
+const std::string |
+XmppPresenceImpl::google_user_id() const { |
+ if (!raw_xml_) |
+ return std::string(); |
+ |
+ XmlElement* muc_user_x = raw_xml_->FirstNamed(QN_MUC_USER_X); |
+ if (muc_user_x) { |
+ XmlElement* muc_user_item = muc_user_x->FirstNamed(QN_MUC_USER_ITEM); |
+ if (muc_user_item) { |
+ return muc_user_item->Attr(QN_GOOGLE_USER_ID); |
+ } |
+ } |
+ |
+ return std::string(); |
+} |
+ |
+const std::string |
+XmppPresenceImpl::nickname() const { |
+ if (!raw_xml_) |
+ return std::string(); |
+ |
+ XmlElement* nickname = raw_xml_->FirstNamed(QN_NICKNAME); |
+ if (nickname) { |
+ return nickname->BodyText(); |
+ } |
+ |
+ return std::string(); |
+} |
+ |
+const XmlElement* |
+XmppPresenceImpl::raw_xml() const { |
+ if (!raw_xml_) |
+ const_cast<XmppPresenceImpl*>(this)->CreateRawXmlSkeleton(); |
+ return raw_xml_.get(); |
+} |
+ |
+XmppReturnStatus |
+XmppPresenceImpl::set_raw_xml(const XmlElement * xml) { |
+ if (!xml || |
+ xml->Name() != QN_PRESENCE) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ raw_xml_.reset(new XmlElement(*xml)); |
+ return XMPP_RETURN_OK; |
+} |
+ |
+void |
+XmppPresenceImpl::CreateRawXmlSkeleton() { |
+ raw_xml_.reset(new XmlElement(QN_PRESENCE)); |
+} |
+ |
+// XmppRosterContactImpl ------------------------------------------------------- |
+XmppRosterContact* |
+XmppRosterContact::Create() { |
+ return new XmppRosterContactImpl(); |
+} |
+ |
+XmppRosterContactImpl::XmppRosterContactImpl() { |
+ ResetGroupCache(); |
+} |
+ |
+void |
+XmppRosterContactImpl::SetXmlFromWire(const XmlElement* xml) { |
+ ResetGroupCache(); |
+ if (xml) |
+ raw_xml_.reset(new XmlElement(*xml)); |
+ else |
+ raw_xml_.reset(NULL); |
+} |
+ |
+void |
+XmppRosterContactImpl::ResetGroupCache() { |
+ group_count_ = -1; |
+ group_index_returned_ = -1; |
+ group_returned_ = NULL; |
+} |
+ |
+const Jid |
+XmppRosterContactImpl::jid() const { |
+ return Jid(raw_xml_->Attr(QN_JID)); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterContactImpl::set_jid(const Jid& jid) |
+{ |
+ if (!raw_xml_) |
+ CreateRawXmlSkeleton(); |
+ |
+ if (!jid.IsValid()) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ raw_xml_->SetAttr(QN_JID, jid.Str()); |
+ |
+ return XMPP_RETURN_OK; |
+} |
+ |
+const std::string |
+XmppRosterContactImpl::name() const { |
+ return raw_xml_->Attr(QN_NAME); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterContactImpl::set_name(const std::string& name) { |
+ if (!raw_xml_) |
+ CreateRawXmlSkeleton(); |
+ |
+ if (name == STR_EMPTY) |
+ raw_xml_->ClearAttr(QN_NAME); |
+ else |
+ raw_xml_->SetAttr(QN_NAME, name); |
+ |
+ return XMPP_RETURN_OK; |
+} |
+ |
+XmppSubscriptionState |
+XmppRosterContactImpl::subscription_state() const { |
+ if (!raw_xml_) |
+ return XMPP_SUBSCRIPTION_NONE; |
+ |
+ XmppSubscriptionState state = XMPP_SUBSCRIPTION_NONE; |
+ |
+ if (StringToSubscriptionState(raw_xml_->Attr(QN_SUBSCRIPTION), |
+ raw_xml_->Attr(QN_ASK), |
+ &state)) |
+ return state; |
+ |
+ return XMPP_SUBSCRIPTION_NONE; |
+} |
+ |
+size_t |
+XmppRosterContactImpl::GetGroupCount() const { |
+ if (!raw_xml_) |
+ return 0; |
+ |
+ if (-1 == group_count_) { |
+ XmlElement *group_element = raw_xml_->FirstNamed(QN_ROSTER_GROUP); |
+ int group_count = 0; |
+ while(group_element) { |
+ group_count++; |
+ group_element = group_element->NextNamed(QN_ROSTER_GROUP); |
+ } |
+ |
+ ASSERT(group_count > 0); // protect the cast |
+ XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this); |
+ me->group_count_ = group_count; |
+ } |
+ |
+ return group_count_; |
+} |
+ |
+const std::string |
+XmppRosterContactImpl::GetGroup(size_t index) const { |
+ if (index >= GetGroupCount()) |
+ return STR_EMPTY; |
+ |
+ // We cache the last group index and element that we returned. This way |
+ // going through the groups in order is order n and not n^2. This could be |
+ // enhanced if necessary by starting at the cached value if the index asked |
+ // is after the cached one. |
+ if (group_index_returned_ >= 0 && |
+ index == static_cast<size_t>(group_index_returned_) + 1) |
+ { |
+ XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this); |
+ me->group_returned_ = group_returned_->NextNamed(QN_ROSTER_GROUP); |
+ ASSERT(group_returned_ != NULL); |
+ me->group_index_returned_++; |
+ } else if (group_index_returned_ < 0 || |
+ static_cast<size_t>(group_index_returned_) != index) { |
+ XmlElement * group_element = raw_xml_->FirstNamed(QN_ROSTER_GROUP); |
+ size_t group_index = 0; |
+ while(group_index < index) { |
+ ASSERT(group_element != NULL); |
+ group_index++; |
+ group_element = group_element->NextNamed(QN_ROSTER_GROUP); |
+ } |
+ |
+ XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this); |
+ me->group_index_returned_ = static_cast<int>(group_index); |
+ me->group_returned_ = group_element; |
+ } |
+ |
+ return group_returned_->BodyText(); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterContactImpl::AddGroup(const std::string& group) { |
+ if (group == STR_EMPTY) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ if (!raw_xml_) |
+ CreateRawXmlSkeleton(); |
+ |
+ if (FindGroup(group, NULL, NULL)) |
+ return XMPP_RETURN_OK; |
+ |
+ raw_xml_->AddElement(new XmlElement(QN_ROSTER_GROUP)); |
+ raw_xml_->AddText(group, 1); |
+ ++group_count_; |
+ |
+ return XMPP_RETURN_OK; |
+} |
+ |
+XmppReturnStatus |
+XmppRosterContactImpl::RemoveGroup(const std::string& group) { |
+ if (group == STR_EMPTY) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ if (!raw_xml_) |
+ return XMPP_RETURN_OK; |
+ |
+ XmlChild * child_before; |
+ if (FindGroup(group, NULL, &child_before)) { |
+ raw_xml_->RemoveChildAfter(child_before); |
+ ResetGroupCache(); |
+ } |
+ return XMPP_RETURN_OK; |
+} |
+ |
+bool |
+XmppRosterContactImpl::FindGroup(const std::string& group, |
+ XmlElement** element, |
+ XmlChild** child_before) { |
+ XmlChild * prev_child = NULL; |
+ XmlChild * next_child; |
+ XmlChild * child; |
+ for (child = raw_xml_->FirstChild(); child; child = next_child) { |
+ next_child = child->NextChild(); |
+ if (!child->IsText() && |
+ child->AsElement()->Name() == QN_ROSTER_GROUP && |
+ child->AsElement()->BodyText() == group) { |
+ if (element) |
+ *element = child->AsElement(); |
+ if (child_before) |
+ *child_before = prev_child; |
+ return true; |
+ } |
+ prev_child = child; |
+ } |
+ |
+ return false; |
+} |
+ |
+const XmlElement* |
+XmppRosterContactImpl::raw_xml() const { |
+ if (!raw_xml_) |
+ const_cast<XmppRosterContactImpl*>(this)->CreateRawXmlSkeleton(); |
+ return raw_xml_.get(); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterContactImpl::set_raw_xml(const XmlElement* xml) { |
+ if (!xml || |
+ xml->Name() != QN_ROSTER_ITEM || |
+ xml->HasAttr(QN_SUBSCRIPTION) || |
+ xml->HasAttr(QN_ASK)) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ ResetGroupCache(); |
+ |
+ raw_xml_.reset(new XmlElement(*xml)); |
+ |
+ return XMPP_RETURN_OK; |
+} |
+ |
+void |
+XmppRosterContactImpl::CreateRawXmlSkeleton() { |
+ raw_xml_.reset(new XmlElement(QN_ROSTER_ITEM)); |
+} |
+ |
+// XmppRosterModuleImpl -------------------------------------------------------- |
+XmppRosterModule * |
+XmppRosterModule::Create() { |
+ return new XmppRosterModuleImpl(); |
+} |
+ |
+XmppRosterModuleImpl::XmppRosterModuleImpl() : |
+ roster_handler_(NULL), |
+ incoming_presence_map_(new JidPresenceVectorMap()), |
+ incoming_presence_vector_(new PresenceVector()), |
+ contacts_(new ContactVector()) { |
+ |
+} |
+ |
+XmppRosterModuleImpl::~XmppRosterModuleImpl() { |
+ DeleteIncomingPresence(); |
+ DeleteContacts(); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::set_roster_handler(XmppRosterHandler * handler) { |
+ roster_handler_ = handler; |
+ return XMPP_RETURN_OK; |
+} |
+ |
+XmppRosterHandler* |
+XmppRosterModuleImpl::roster_handler() { |
+ return roster_handler_; |
+} |
+ |
+XmppPresence* |
+XmppRosterModuleImpl::outgoing_presence() { |
+ return &outgoing_presence_; |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::BroadcastPresence() { |
+ // Scrub the outgoing presence |
+ const XmlElement* element = outgoing_presence_.raw_xml(); |
+ |
+ ASSERT(!element->HasAttr(QN_TO) && |
+ !element->HasAttr(QN_FROM) && |
+ (element->Attr(QN_TYPE) == STR_EMPTY || |
+ element->Attr(QN_TYPE) == "unavailable")); |
+ |
+ if (!engine()) |
+ return XMPP_RETURN_BADSTATE; |
+ |
+ return engine()->SendStanza(element); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::SendDirectedPresence(const XmppPresence* presence, |
+ const Jid& to_jid) { |
+ if (!presence) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ if (!engine()) |
+ return XMPP_RETURN_BADSTATE; |
+ |
+ XmlElement element(*(presence->raw_xml())); |
+ |
+ if (element.Name() != QN_PRESENCE || |
+ element.HasAttr(QN_TO) || |
+ element.HasAttr(QN_FROM)) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ if (element.HasAttr(QN_TYPE)) { |
+ if (element.Attr(QN_TYPE) != STR_EMPTY && |
+ element.Attr(QN_TYPE) != "unavailable") { |
+ return XMPP_RETURN_BADARGUMENT; |
+ } |
+ } |
+ |
+ element.SetAttr(QN_TO, to_jid.Str()); |
+ |
+ return engine()->SendStanza(&element); |
+} |
+ |
+size_t |
+XmppRosterModuleImpl::GetIncomingPresenceCount() { |
+ return incoming_presence_vector_->size(); |
+} |
+ |
+const XmppPresence* |
+XmppRosterModuleImpl::GetIncomingPresence(size_t index) { |
+ if (index >= incoming_presence_vector_->size()) |
+ return NULL; |
+ return (*incoming_presence_vector_)[index]; |
+} |
+ |
+size_t |
+XmppRosterModuleImpl::GetIncomingPresenceForJidCount(const Jid& jid) |
+{ |
+ // find the vector in the map |
+ JidPresenceVectorMap::iterator pos; |
+ pos = incoming_presence_map_->find(jid); |
+ if (pos == incoming_presence_map_->end()) |
+ return 0; |
+ |
+ ASSERT(pos->second != NULL); |
+ |
+ return pos->second->size(); |
+} |
+ |
+const XmppPresence* |
+XmppRosterModuleImpl::GetIncomingPresenceForJid(const Jid& jid, |
+ size_t index) { |
+ JidPresenceVectorMap::iterator pos; |
+ pos = incoming_presence_map_->find(jid); |
+ if (pos == incoming_presence_map_->end()) |
+ return NULL; |
+ |
+ ASSERT(pos->second != NULL); |
+ |
+ if (index >= pos->second->size()) |
+ return NULL; |
+ |
+ return (*pos->second)[index]; |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::RequestRosterUpdate() { |
+ if (!engine()) |
+ return XMPP_RETURN_BADSTATE; |
+ |
+ XmlElement roster_get(QN_IQ); |
+ roster_get.AddAttr(QN_TYPE, "get"); |
+ roster_get.AddAttr(QN_ID, engine()->NextId()); |
+ roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true)); |
+ return engine()->SendIq(&roster_get, this, NULL); |
+} |
+ |
+size_t |
+XmppRosterModuleImpl::GetRosterContactCount() { |
+ return contacts_->size(); |
+} |
+ |
+const XmppRosterContact* |
+XmppRosterModuleImpl::GetRosterContact(size_t index) { |
+ if (index >= contacts_->size()) |
+ return NULL; |
+ return (*contacts_)[index]; |
+} |
+ |
+class RosterPredicate { |
+public: |
+ explicit RosterPredicate(const Jid& jid) : jid_(jid) { |
+ } |
+ |
+ bool operator() (XmppRosterContactImpl *& contact) { |
+ return contact->jid() == jid_; |
+ } |
+ |
+private: |
+ Jid jid_; |
+}; |
+ |
+const XmppRosterContact* |
+XmppRosterModuleImpl::FindRosterContact(const Jid& jid) { |
+ ContactVector::iterator pos; |
+ |
+ pos = std::find_if(contacts_->begin(), |
+ contacts_->end(), |
+ RosterPredicate(jid)); |
+ if (pos == contacts_->end()) |
+ return NULL; |
+ |
+ return *pos; |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::RequestRosterChange( |
+ const XmppRosterContact* contact) { |
+ if (!contact) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ Jid jid = contact->jid(); |
+ |
+ if (!jid.IsValid()) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ if (!engine()) |
+ return XMPP_RETURN_BADSTATE; |
+ |
+ const XmlElement* contact_xml = contact->raw_xml(); |
+ if (contact_xml->Name() != QN_ROSTER_ITEM || |
+ contact_xml->HasAttr(QN_SUBSCRIPTION) || |
+ contact_xml->HasAttr(QN_ASK)) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ XmlElement roster_add(QN_IQ); |
+ roster_add.AddAttr(QN_TYPE, "set"); |
+ roster_add.AddAttr(QN_ID, engine()->NextId()); |
+ roster_add.AddElement(new XmlElement(QN_ROSTER_QUERY, true)); |
+ roster_add.AddElement(new XmlElement(*contact_xml), 1); |
+ |
+ return engine()->SendIq(&roster_add, this, NULL); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::RequestRosterRemove(const Jid& jid) { |
+ if (!jid.IsValid()) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ if (!engine()) |
+ return XMPP_RETURN_BADSTATE; |
+ |
+ XmlElement roster_add(QN_IQ); |
+ roster_add.AddAttr(QN_TYPE, "set"); |
+ roster_add.AddAttr(QN_ID, engine()->NextId()); |
+ roster_add.AddElement(new XmlElement(QN_ROSTER_QUERY, true)); |
+ roster_add.AddAttr(QN_JID, jid.Str(), 1); |
+ roster_add.AddAttr(QN_SUBSCRIPTION, "remove", 1); |
+ |
+ return engine()->SendIq(&roster_add, this, NULL); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::RequestSubscription(const Jid& jid) { |
+ return SendSubscriptionRequest(jid, "subscribe"); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::CancelSubscription(const Jid& jid) { |
+ return SendSubscriptionRequest(jid, "unsubscribe"); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::ApproveSubscriber(const Jid& jid) { |
+ return SendSubscriptionRequest(jid, "subscribed"); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::CancelSubscriber(const Jid& jid) { |
+ return SendSubscriptionRequest(jid, "unsubscribed"); |
+} |
+ |
+void |
+XmppRosterModuleImpl::IqResponse(XmppIqCookie, const XmlElement * stanza) { |
+ // The only real Iq response that we expect to recieve are initial roster |
+ // population |
+ if (stanza->Attr(QN_TYPE) == "error") |
+ { |
+ if (roster_handler_) |
+ roster_handler_->RosterError(this, stanza); |
+ |
+ return; |
+ } |
+ |
+ ASSERT(stanza->Attr(QN_TYPE) == "result"); |
+ |
+ InternalRosterItems(stanza); |
+} |
+ |
+bool |
+XmppRosterModuleImpl::HandleStanza(const XmlElement * stanza) |
+{ |
+ ASSERT(engine() != NULL); |
+ |
+ // There are two types of stanzas that we care about: presence and roster push |
+ // Iqs |
+ if (stanza->Name() == QN_PRESENCE) { |
+ const std::string& jid_string = stanza->Attr(QN_FROM); |
+ Jid jid(jid_string); |
+ |
+ if (!jid.IsValid()) |
+ return false; // if the Jid isn't valid, don't process |
+ |
+ const std::string& type = stanza->Attr(QN_TYPE); |
+ XmppSubscriptionRequestType request_type; |
+ if (StringToSubscriptionRequestType(type, &request_type)) |
+ InternalSubscriptionRequest(jid, stanza, request_type); |
+ else if (type == "unavailable" || type == STR_EMPTY) |
+ InternalIncomingPresence(jid, stanza); |
+ else if (type == "error") |
+ InternalIncomingPresenceError(jid, stanza); |
+ else |
+ return false; |
+ |
+ return true; |
+ } else if (stanza->Name() == QN_IQ) { |
+ const XmlElement * roster_query = stanza->FirstNamed(QN_ROSTER_QUERY); |
+ if (!roster_query || stanza->Attr(QN_TYPE) != "set") |
+ return false; |
+ |
+ InternalRosterItems(stanza); |
+ |
+ // respond to the IQ |
+ XmlElement result(QN_IQ); |
+ result.AddAttr(QN_TYPE, "result"); |
+ result.AddAttr(QN_TO, stanza->Attr(QN_FROM)); |
+ result.AddAttr(QN_ID, stanza->Attr(QN_ID)); |
+ |
+ engine()->SendStanza(&result); |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+void |
+XmppRosterModuleImpl::DeleteIncomingPresence() { |
+ // Clear out the vector of all presence notifications |
+ { |
+ PresenceVector::iterator pos; |
+ for (pos = incoming_presence_vector_->begin(); |
+ pos < incoming_presence_vector_->end(); |
+ ++pos) { |
+ XmppPresenceImpl * presence = *pos; |
+ *pos = NULL; |
+ delete presence; |
+ } |
+ incoming_presence_vector_->clear(); |
+ } |
+ |
+ // Clear out all of the small presence vectors per Jid |
+ { |
+ JidPresenceVectorMap::iterator pos; |
+ for (pos = incoming_presence_map_->begin(); |
+ pos != incoming_presence_map_->end(); |
+ ++pos) { |
+ PresenceVector* presence_vector = pos->second; |
+ pos->second = NULL; |
+ delete presence_vector; |
+ } |
+ incoming_presence_map_->clear(); |
+ } |
+} |
+ |
+void |
+XmppRosterModuleImpl::DeleteContacts() { |
+ ContactVector::iterator pos; |
+ for (pos = contacts_->begin(); |
+ pos < contacts_->end(); |
+ ++pos) { |
+ XmppRosterContact* contact = *pos; |
+ *pos = NULL; |
+ delete contact; |
+ } |
+ contacts_->clear(); |
+} |
+ |
+XmppReturnStatus |
+XmppRosterModuleImpl::SendSubscriptionRequest(const Jid& jid, |
+ const std::string& type) { |
+ if (!jid.IsValid()) |
+ return XMPP_RETURN_BADARGUMENT; |
+ |
+ if (!engine()) |
+ return XMPP_RETURN_BADSTATE; |
+ |
+ XmlElement presence_request(QN_PRESENCE); |
+ presence_request.AddAttr(QN_TO, jid.Str()); |
+ presence_request.AddAttr(QN_TYPE, type); |
+ |
+ return engine()->SendStanza(&presence_request); |
+} |
+ |
+ |
+void |
+XmppRosterModuleImpl::InternalSubscriptionRequest(const Jid& jid, |
+ const XmlElement* stanza, |
+ XmppSubscriptionRequestType |
+ request_type) { |
+ if (roster_handler_) |
+ roster_handler_->SubscriptionRequest(this, jid, request_type, stanza); |
+} |
+ |
+class PresencePredicate { |
+public: |
+ explicit PresencePredicate(const Jid& jid) : jid_(jid) { |
+ } |
+ |
+ bool operator() (XmppPresenceImpl *& contact) { |
+ return contact->jid() == jid_; |
+ } |
+ |
+private: |
+ Jid jid_; |
+}; |
+ |
+void |
+XmppRosterModuleImpl::InternalIncomingPresence(const Jid& jid, |
+ const XmlElement* stanza) { |
+ bool added = false; |
+ Jid bare_jid = jid.BareJid(); |
+ |
+ // First add the presence to the map |
+ JidPresenceVectorMap::iterator pos; |
+ pos = incoming_presence_map_->find(jid.BareJid()); |
+ if (pos == incoming_presence_map_->end()) { |
+ // Insert a new entry into the map. Get the position of this new entry |
+ pos = (incoming_presence_map_->insert( |
+ std::make_pair(bare_jid, new PresenceVector()))).first; |
+ } |
+ |
+ PresenceVector * presence_vector = pos->second; |
+ ASSERT(presence_vector != NULL); |
+ |
+ // Try to find this jid in the bare jid bucket |
+ PresenceVector::iterator presence_pos; |
+ XmppPresenceImpl* presence; |
+ presence_pos = std::find_if(presence_vector->begin(), |
+ presence_vector->end(), |
+ PresencePredicate(jid)); |
+ |
+ // Update/add it to the bucket |
+ if (presence_pos == presence_vector->end()) { |
+ presence = new XmppPresenceImpl(); |
+ if (XMPP_RETURN_OK == presence->set_raw_xml(stanza)) { |
+ added = true; |
+ presence_vector->push_back(presence); |
+ } else { |
+ delete presence; |
+ presence = NULL; |
+ } |
+ } else { |
+ presence = *presence_pos; |
+ presence->set_raw_xml(stanza); |
+ } |
+ |
+ // now add to the comprehensive vector |
+ if (added) |
+ incoming_presence_vector_->push_back(presence); |
+ |
+ // Call back to the user with the changed presence information |
+ if (roster_handler_) |
+ roster_handler_->IncomingPresenceChanged(this, presence); |
+} |
+ |
+ |
+void |
+XmppRosterModuleImpl::InternalIncomingPresenceError(const Jid& jid, |
+ const XmlElement* stanza) { |
+ if (roster_handler_) |
+ roster_handler_->SubscriptionError(this, jid, stanza); |
+} |
+ |
+void |
+XmppRosterModuleImpl::InternalRosterItems(const XmlElement* stanza) { |
+ const XmlElement* result_data = stanza->FirstNamed(QN_ROSTER_QUERY); |
+ if (!result_data) |
+ return; // unknown stuff in result! |
+ |
+ bool all_new = contacts_->empty(); |
+ |
+ for (const XmlElement* roster_item = result_data->FirstNamed(QN_ROSTER_ITEM); |
+ roster_item; |
+ roster_item = roster_item->NextNamed(QN_ROSTER_ITEM)) |
+ { |
+ const std::string& jid_string = roster_item->Attr(QN_JID); |
+ Jid jid(jid_string); |
+ if (!jid.IsValid()) |
+ continue; |
+ |
+ // This algorithm is N^2 on the number of incoming contacts after the |
+ // initial load. There is no way to do this faster without allowing |
+ // duplicates, introducing more data structures or write a custom data |
+ // structure. We'll see if this becomes a perf problem and fix it if it |
+ // does. |
+ ContactVector::iterator pos = contacts_->end(); |
+ |
+ if (!all_new) { |
+ pos = std::find_if(contacts_->begin(), |
+ contacts_->end(), |
+ RosterPredicate(jid)); |
+ } |
+ |
+ if (pos != contacts_->end()) { // Update/remove a current contact |
+ if (roster_item->Attr(QN_SUBSCRIPTION) == "remove") { |
+ XmppRosterContact* contact = *pos; |
+ contacts_->erase(pos); |
+ if (roster_handler_) |
+ roster_handler_->ContactRemoved(this, contact, |
+ std::distance(contacts_->begin(), pos)); |
+ delete contact; |
+ } else { |
+ XmppRosterContact* old_contact = *pos; |
+ *pos = new XmppRosterContactImpl(); |
+ (*pos)->SetXmlFromWire(roster_item); |
+ if (roster_handler_) |
+ roster_handler_->ContactChanged(this, old_contact, |
+ std::distance(contacts_->begin(), pos)); |
+ delete old_contact; |
+ } |
+ } else { // Add a new contact |
+ XmppRosterContactImpl* contact = new XmppRosterContactImpl(); |
+ contact->SetXmlFromWire(roster_item); |
+ contacts_->push_back(contact); |
+ if (roster_handler_ && !all_new) |
+ roster_handler_->ContactsAdded(this, contacts_->size() - 1, 1); |
+ } |
+ } |
+ |
+ // Send a consolidated update if all contacts are new |
+ if (roster_handler_ && all_new) |
+ roster_handler_->ContactsAdded(this, 0, contacts_->size()); |
+} |
+ |
+} |