| Index: third_party/xmpp/rostermoduleimpl.cc
 | 
| diff --git a/third_party/xmpp/rostermoduleimpl.cc b/third_party/xmpp/rostermoduleimpl.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..cd652b388f5fe7c598d40065691625d827dc603f
 | 
| --- /dev/null
 | 
| +++ b/third_party/xmpp/rostermoduleimpl.cc
 | 
| @@ -0,0 +1,1058 @@
 | 
| +// Copyright 2004 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include <algorithm>
 | 
| +#include <iostream>
 | 
| +#include <map>
 | 
| +#include <sstream>
 | 
| +#include <string>
 | 
| +#include <vector>
 | 
| +#include "third_party/xmpp/constants.h"
 | 
| +#include "third_party/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());
 | 
| +}
 | 
| +
 | 
| +}
 | 
| 
 |