webrtc/talk/xmpp/rostermoduleimpl.cc
2013-09-19 05:49:50 +00:00

1086 lines
28 KiB
C++

/*
* libjingle
* Copyright 2004--2005, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include <sstream>
#include <iostream>
#include "talk/base/common.h"
#include "talk/base/stringencode.h"
#include "talk/xmpp/constants.h"
#include "talk/xmpp/rostermoduleimpl.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 (!talk_base::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 (talk_base::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;
const std::string& type = xml->Attr(QN_TYPE);
if (type != STR_EMPTY && type != "unavailable")
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());
}
}