| 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());
|
| +}
|
| +
|
| +}
|
|
|