Main Page   Namespace List   Class Hierarchy   Compound List   File List   Compound Members  

jabberoo-roster.cc

00001 /* jabberoo-roster.cc
00002  * Jabber Roster handler
00003  *
00004  * Original Code Copyright (C) 1999-2001 Dave Smith (dave@jabber.org)
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  * 
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  * 
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  * Contributor(s): Julian Missig
00021  *
00022  * This Original Code has been modified by IBM Corporation. Modifications 
00023  * made by IBM described herein are Copyright (c) International Business 
00024  * Machines Corporation, 2002.
00025  *
00026  * Date             Modified by     Description of modification
00027  * 01/20/2002       IBM Corp.       Updated to libjudo 1.1.1
00028  * 2002-03-05       IBM Corp.       Updated to libjudo 1.1.5
00029  */
00030 
00031 #include <jabberoo.hh>
00032 using namespace jabberoo;
00033 
00034 Roster::Roster(Session& s)
00035      : _owner(s)
00036 {}
00037 
00038 const Roster::Item& Roster::operator[](const string& jid) const
00039 {
00040      ItemMap::const_iterator it = _items.find(filterJID(jid));
00041      if (it == _items.end())
00042           throw XCP_InvalidJID();
00043      else
00044           return it->second;
00045 }
00046 
00047 void Roster::reset()
00048 {
00049      _items.clear();
00050 }
00051 
00052 // ---------------------------------------------------------
00053 // Information
00054 // ---------------------------------------------------------
00055 bool Roster::containsJID(const string& jid) const
00056 {
00057      return (_items.find(filterJID(jid)) != _items.end());
00058 }
00059 
00060 // ---------------------------------------------------------
00061 // Update ops
00062 // ---------------------------------------------------------
00063 void Roster::update(const Element& t)
00064 {
00065      bool updateFlag = false;
00066 
00067      // Process each <item> tag and add/update
00068      // the roster
00069      Element::const_iterator it = t.begin();
00070      for (; it != t.end(); ++it )
00071      {
00072           if ((*it)->getType() != Node::ntElement)
00073                continue;
00074           // Cast item into a tag..
00075           Element& item = *static_cast<Element*>(*it);
00076 
00077           // Extract JID & resource
00078           string jid = filterJID(item.getAttrib("jid"));
00079 
00080           // Lookup this jid in the item map
00081           ItemMap::iterator rit = _items.find(jid);
00082 
00083           // If this jid is already in the Item map, update it...
00084           if (rit != _items.end())
00085           {
00086                // If the subscription type = "remove" then, we need
00087                // to delete this roster item...
00088                updateFlag = true;
00089                if (item.cmpAttrib("subscription", "remove"))
00090                {
00091                     removeItemFromAllGroups(rit->second);
00092                     _items.erase(rit);
00093                }
00094                // Otherwise, update the roster item
00095                else
00096                     rit->second.update(*this, item);
00097 
00098           }
00099           // Otherwise, create a new item on the map
00100           else if (!item.cmpAttrib("subscription", "remove"))
00101           {
00102                _items.insert(make_pair(jid, Item(*this, item)));
00103                updateFlag = true;
00104           }
00105      }
00106      // Notify whoever we need to that the overall roster has been updated
00107      if (updateFlag)
00108           evtRefresh();
00109 
00110 }
00111 
00112 void Roster::update(const Presence& p, Presence::Type prev_type)
00113 {
00114      // Locate the presence sender on our map
00115      string jid = filterJID(p.getFrom());
00116      ItemMap::iterator it = _items.find(jid);
00117      if (it != _items.end())
00118      {
00119           it->second.update(*this, jid, p, prev_type);             // Update the item
00120      }
00121 }
00122 
00123 void Roster::update(const Item& i)
00124 {
00125      // Compose basic query packet
00126      Element iq("iq");
00127      iq.putAttrib("type", "set");
00128      Element* query = iq.addElement("query");
00129      query->putAttrib("xmlns", "jabber:iq:roster");
00130      Element* item = query->addElement("item");
00131 
00132      // Insert item specifics
00133      item->putAttrib("jid", i.getJID());
00134      if (!i.getNickname().empty())
00135           item->putAttrib("name", i.getNickname());
00136      // Add all groups in this item
00137      for (Item::iterator it = i.begin(); it != i.end(); it++)
00138           item->addElement("group", *it);
00139 
00140      // Transmit item
00141      _owner << iq.toString().c_str();
00142 }
00143 
00144 // ---------------------------------------------------------
00145 // Control ops
00146 // ---------------------------------------------------------
00147 void Roster::deleteUser(const string& jid)
00148 {
00149 
00150      // Send a subscription=remove request...
00151      Element iq("iq");
00152      iq.putAttrib("type", "set");
00153      Element* query = iq.addElement("query");
00154      query->putAttrib("xmlns", "jabber:iq:roster");
00155      Element* item = query->addElement("item");
00156      item->putAttrib("jid", jid);
00157      item->putAttrib("subscription", "remove");
00158 
00159      // Send it
00160      _owner << iq.toString().c_str() << Presence(jid, Presence::ptUnsubRequest);
00161 
00162      // If the user is an Agent then send a remove request
00163      if ((jid.find("@") == string::npos) && (jid.find("/") != string::npos))
00164           _owner.queryNamespace("jabber:iq:register", slot(*this, &Roster::deleteAgent), jid);
00165 }
00166 
00167 void Roster::deleteAgent(const Element& iq)
00168 {
00169      const Element* query = iq.findElement("query");
00170 
00171      Element niq("iq");
00172      niq.putAttrib("type", "set");
00173      niq.putAttrib("to", iq.getAttrib("from"));
00174      Element* nquery = niq.addElement("query");
00175      nquery->putAttrib("xmlns", "jabber:iq:register");
00176      nquery->addElement("key", query->getChildCData("key"));
00177      nquery->addElement("remove");
00178 
00179      // send iit
00180      _owner << niq.toString().c_str();
00181 }
00182 
00183 void Roster::fetch() const
00184 {
00185      Element iq("iq");
00186      iq.putAttrib("type", "get");
00187      Element* query = iq.addElement("query");
00188      query->putAttrib("xmlns", "jabber:iq:roster");
00189 
00190      _owner << iq.toString().c_str();
00191 }
00192 
00193 // ---------------------------------------------------------
00194 // S10N translation
00195 // ---------------------------------------------------------
00196 string Roster::translateS10N(Subscription stype)
00197 {
00198      switch (stype)
00199      {
00200      case rsNone:   return "none";
00201      case rsTo:     return "to";
00202      case rsFrom:   return "from";
00203      case rsBoth:   return "both";
00204      case rsRemove: return "remove";
00205      }
00206      return "none";
00207 }
00208 
00209 Roster::Subscription Roster::translateS10N(const string& stype)
00210 {
00211      if (stype == "to")
00212           return rsTo;
00213      else if (stype == "from")
00214           return rsFrom;
00215      else if (stype == "both")
00216           return rsBoth;
00217      else if (stype == "remove")
00218           return rsRemove;
00219      else
00220           return rsNone;
00221 }
00222 
00223 string Roster::filterJID(const string& jid)
00224 {
00225      // If this jid doesn't have a user, then
00226      // escape the whole jid and return
00227      if (jid.find("@") == string::npos)
00228           return escape(jid);
00229      // Otherwise, escape and return just user@host
00230      else
00231           return JID::getUserHost(jid);
00232      
00233 }
00234 
00235 void Roster::removeItemFromGroup(const string& group, const string& jid)
00236 {
00237      typedef map<string, set<string> >::iterator GIT;
00238      GIT it = _groups.find(group);
00239      // Lookup this group..if found..
00240      if (it != _groups.end())
00241      {
00242           // Erase the JID in the group-set
00243           set<string>::iterator member = it->second.find(jid);
00244           if (member != it->second.end()) 
00245           {
00246                it->second.erase(member);
00247           } 
00248           // Erase the group-set if it's empty n
00249           if (it->second.empty())
00250                _groups.erase(it);
00251      }
00252 }
00253 
00254 void Roster::removeItemFromAllGroups(const Item& item)
00255 {
00256      // Remove this item from all groups
00257      for (Item::iterator it = item.begin(); it != item.end(); it++)
00258      {
00259           removeItemFromGroup(*it, item.getJID());
00260      }
00261 }
00262 
00263 void Roster::mergeItemGroups(const string& itemjid, const set<string>& oldgrp, const set<string>& newgrp)
00264 {
00265      // Walk the group list and update Roster::_groups
00266      // accordingly..icky..slow..wish there was a better
00267      // way
00268      set<string>::const_iterator new_it = newgrp.begin();
00269      set<string>::const_iterator old_it = oldgrp.begin();
00270      while ((new_it != newgrp.end()) || (old_it != oldgrp.end()))
00271      {
00272           if ((old_it == oldgrp.end()) && (new_it != newgrp.end()))
00273           {
00274                _groups[*new_it].insert(itemjid);
00275                ++new_it;
00276           }
00277           else if ((old_it != oldgrp.end()) && (new_it == newgrp.end()))
00278           {
00279                removeItemFromGroup(*old_it, itemjid);
00280                ++old_it;
00281           }
00282           else if ((*old_it) == (*new_it))
00283           {
00284                ++old_it; 
00285                ++new_it;
00286           }
00287           else
00288           {
00289                removeItemFromGroup(*old_it, itemjid);
00290                ++old_it;
00291           }
00292      }
00293 
00294 }
00295 
00296 // ---------------------------------------------------------
00297 // IMPLEMENTATION
00298 // 
00299 // Roster::Item
00300 // ---------------------------------------------------------
00301 Roster::Item::Item(const Element& t)
00302      : _rescnt(0)
00303 {
00304      update(t);
00305 }
00306 
00307 Roster::Item::Item(Roster& r, const Element& t)
00308      : _rescnt(0)
00309 {
00310      update(r, t);
00311 }
00312 
00313 Roster::Item::Item(const string& jid, const string& nickname)
00314      : _jid(jid), _nickname(nickname)
00315 {}
00316 
00317 Roster::Item::~Item()
00318 {}
00319 
00320 void Roster::Item::addToGroup(const string& group)
00321 {
00322      _groups.insert(group);
00323 }
00324 
00325 void Roster::Item::delFromGroup(const string& group)
00326 {
00327      _groups.erase(group);
00328 }
00329 
00330 void Roster::Item::clearGroups()
00331 {
00332      _groups.clear();
00333 }
00334 
00335 bool Roster::Item::update(const Element& t)
00336 {
00337      _jid = t.getAttrib("jid");
00338 
00339      // If no nickname is available, use standard user@host
00340      _nickname = t.getAttrib("name");
00341      if (_nickname == "")
00342           _nickname = _jid;
00343           
00344      // Get subscription
00345      _type = Roster::translateS10N(t.getAttrib("subscription"));    
00346      
00347      // Determine if subscription is pending
00348      _pending = (t.getAttrib("ask") == "subscribe");
00349 
00350      _groups.clear();
00351      
00352      // Parse group set from subtags
00353      Element::const_iterator it = t.begin();
00354      for (; it != t.end(); ++it)
00355      {
00356           if ((*it)->getType() != Node::ntElement)
00357                continue;
00358           string grp_name = static_cast<Element*>(*it)->getCDATA();
00359           if (!grp_name.empty())
00360                _groups.insert(grp_name);
00361      }
00362 
00363      // If the subscription is pending, also display them in Pending virtual group
00364      if (_pending)
00365      {
00366           _groups.insert("Pending");
00367      }
00368      // If they're not in a group, display them in Unfiled virtual group
00369      else if (_groups.empty())
00370      {
00371           // If this jid has no user, but *does* have a resource,
00372           // it must be an agent/transport registration
00373           if ((_jid.find("@") == string::npos) && (_jid.find("/") != string::npos))
00374                _groups.insert("Agents");
00375           // Otherwise, it should be displayed in Unfiled
00376           else
00377                _groups.insert("Unfiled");
00378      }
00379 
00380      return true;
00381 }
00382 
00383 bool Roster::Item::update(Roster& r, const Element& t)
00384 {
00385      // Save old group set
00386      set<string> oldGroups = _groups;
00387 
00388      update(t);
00389 
00390      // Have the owner merge it's representation of item groups
00391      // appropriately
00392      r.mergeItemGroups(_jid, oldGroups, _groups);
00393 
00394      return true;
00395 }
00396 
00397 void Roster::Item::update(Roster& r, const string& jid, const Presence& p, Presence::Type prev_type)
00398 {
00399      bool available = r._owner.presenceDB().available(jid);
00400      
00401      if ((available == true) && (_rescnt == 0))
00402      {
00403           ++_rescnt;
00404      }
00405      else if ((available == false) && (_rescnt == 1))
00406      {
00407           --_rescnt;
00408      }
00409      r.evtPresence(jid, (_rescnt != 0), prev_type);
00410 }
00411 
00412 bool Roster::Item::isAvailable() const
00413 {
00414      return (_rescnt > 0);
00415 }
00416 
00417 string Roster::Item::getNickname() const
00418 {
00419      return _nickname;
00420 }
00421 
00422 void Roster::Item::setNickname(const string& nickname)
00423 {
00424      _nickname = nickname;
00425 }
00426 
00427 string Roster::Item::getJID() const
00428 {
00429      return _jid;
00430 }
00431 
00432 void Roster::Item::setJID(const string& jid)
00433 {
00434      _jid = jid;
00435 }
00436 
00437 Roster::Subscription Roster::Item::getSubsType() const
00438 {
00439      return _type;
00440 }
00441 
00442 bool Roster::Item::isPending() const
00443 {
00444      return _pending;
00445 }

Generated at Tue Apr 16 17:09:06 2002 for jabberoo by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001