1065 lines
27 KiB
C++
1065 lines
27 KiB
C++
/*
|
|
* 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());
|
|
}
|
|
|
|
}
|