/* * Tigase Jabber/XMPP Server * Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. Look for COPYING file in the top folder. * If not, see http://www.gnu.org/licenses/. * * $Rev$ * Last modified by $Author$ * $Date$ */ package tigase.xmpp.impl.roster; //~--- non-JDK imports -------------------------------------------------------- import tigase.db.TigaseDBException; import tigase.db.UserRepository; import tigase.server.Packet; import tigase.util.Algorithms; import tigase.xml.Element; import tigase.xml.XMLUtils; import tigase.xmpp.BareJID; import tigase.xmpp.JID; import tigase.xmpp.NoConnectionIdException; import tigase.xmpp.NotAuthorizedException; import tigase.xmpp.StanzaType; import tigase.xmpp.XMPPResourceConnection; //~--- JDK imports ------------------------------------------------------------ import java.util.ArrayList; import java.util.EnumMap; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.logging.Level; import java.util.logging.Logger; //~--- classes ---------------------------------------------------------------- /** * Describe class RosterAbstract here. * * * Created: Thu Sep 4 18:09:52 2008 * * @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a> * @version $Rev$ */ public abstract class RosterAbstract { // Below StateTransition enum is implementation of all below tables // coming from RFC-3921 // Table 1: Recommended handling of outbound "subscribed" stanzas // +----------------------------------------------------------------+ // | EXISTING STATE | ROUTE? | NEW STATE | // +----------------------------------------------------------------+ // | "None" | no | no state change | // | "None + Pending Out" | no | no state change | // | "None + Pending In" | yes | "From" | // | "None + Pending Out/In" | yes | "From + Pending Out" | // | "To" | no | no state change | // | "To + Pending In" | yes | "Both" | // | "From" | no | no state change | // | "From + Pending Out" | no | no state change | // | "Both" | no | no state change | // +----------------------------------------------------------------+ // Table 2: Recommended handling of outbound "unsubscribed" stanzas // +----------------------------------------------------------------+ // | EXISTING STATE | ROUTE? | NEW STATE | // +----------------------------------------------------------------+ // | "None" | no | no state change | // | "None + Pending Out" | no | no state change | // | "None + Pending In" | yes | "None" | // | "None + Pending Out/In" | yes | "None + Pending Out" | // | "To" | no | no state change | // | "To + Pending In" | yes | "To" | // | "From" | yes | "None" | // | "From + Pending Out" | yes | "None + Pending Out" | // | "Both" | yes | "To" | // +----------------------------------------------------------------+ // Table 3: Recommended handling of inbound "subscribe" stanzas // +------------------------------------------------------------------+ // | EXISTING STATE | DELIVER? | NEW STATE | // +------------------------------------------------------------------+ // | "None" | yes | "None + Pending In" | // | "None + Pending Out" | yes | "None + Pending Out/In" | // | "None + Pending In" | no | no state change | // | "None + Pending Out/In" | no | no state change | // | "To" | yes | "To + Pending In" | // | "To + Pending In" | no | no state change | // | "From" | no * | no state change | // | "From + Pending Out" | no * | no state change | // | "Both" | no * | no state change | // +------------------------------------------------------------------+ // Table 4: Recommended handling of inbound "unsubscribe" stanzas // +------------------------------------------------------------------+ // | EXISTING STATE | DELIVER? | NEW STATE | // +------------------------------------------------------------------+ // | "None" | no | no state change | // | "None + Pending Out" | no | no state change | // | "None + Pending In" | yes * | "None" | // | "None + Pending Out/In" | yes * | "None + Pending Out" | // | "To" | no | no state change | // | "To + Pending In" | yes * | "To" | // | "From" | yes * | "None" | // | "From + Pending Out" | yes * | "None + Pending Out" | // | "Both" | yes * | "To" | // +------------------------------------------------------------------+ // Table 5: Recommended handling of inbound "subscribed" stanzas // +------------------------------------------------------------------+ // | EXISTING STATE | DELIVER? | NEW STATE | // +------------------------------------------------------------------+ // | "None" | no | no state change | // | "None + Pending Out" | yes | "To" | // | "None + Pending In" | no | no state change | // | "None + Pending Out/In" | yes | "To + Pending In" | // | "To" | no | no state change | // | "To + Pending In" | no | no state change | // | "From" | no | no state change | // | "From + Pending Out" | yes | "Both" | // | "Both" | no | no state change | // +------------------------------------------------------------------+ // Table 6: Recommended handling of inbound "unsubscribed" stanzas // +------------------------------------------------------------------+ // | EXISTING STATE | DELIVER? | NEW STATE | // +------------------------------------------------------------------+ // | "None" | no | no state change | // | "None + Pending Out" | yes | "None" | // | "None + Pending In" | no | no state change | // | "None + Pending Out/In" | yes | "None + Pending In" | // | "To" | yes | "None" | // | "To + Pending In" | yes | "None + Pending In" | // | "From" | no | no state change | // | "From + Pending Out" | yes | "From" | // | "Both" | yes | "From" | // +------------------------------------------------------------------+ // There are 2 tables missing I think in RFC-3921: // Table 7: Recommended handling of outbound "subscribe" stanzas // +------------------------------------------------------------------+ // | EXISTING STATE | ROUTE? | NEW STATE | // +------------------------------------------------------------------+ // | "None" | yes | "None + Pending Out" | // | "None + Pending Out" | no | no state change | // | "None + Pending In" | yes | "None + Pending Out/In" | // | "None + Pending Out/In" | no | no state change | // | "To" | no | no state change | // | "To + Pending In" | no | no state change | // | "From" | yes | "From + Pending Out" | // | "From + Pending Out" | no | no state change | // | "Both" | no | no state change | // +------------------------------------------------------------------+ // Table 8: Recommended handling of outbound "unsubscribe" stanzas // +------------------------------------------------------------------+ // | EXISTING STATE | ROUTE? | NEW STATE | // +------------------------------------------------------------------+ // | "None" | no | no state change | // | "None + Pending Out" | yes | "None" | // | "None + Pending In" | no | no state change | // | "None + Pending Out/In" | yes | "None + Pending In" | // | "To" | yes | "None" | // | "To + Pending In" | yes | "None + Pending In" | // | "From" | no | no state change | // | "From + Pending Out" | yes | "From" | // | "Both" | yes | "From" | // +------------------------------------------------------------------+ /** * Enum description * */ public enum StateTransition { none(SubscriptionType.none, // Table 1. SubscriptionType.none, // Table 2. SubscriptionType.none_pending_in, // Table 3. SubscriptionType.none, // Table 4. SubscriptionType.none, // Table 5. SubscriptionType.none, // Table 6. SubscriptionType.none_pending_out, // Table 7. SubscriptionType.none // Table 8. ), none_pending_out(SubscriptionType.none_pending_out, // Table 1. SubscriptionType.none_pending_out, // Table 2. SubscriptionType.none_pending_out_in, // Table 3. SubscriptionType.none_pending_out, // Table 4. SubscriptionType.to, // Table 5. SubscriptionType.none, // Table 6. SubscriptionType.none_pending_out, // Table 7. SubscriptionType.none // Table 8. ), none_pending_in(SubscriptionType.from, // Table 1. SubscriptionType.none, // Table 2. SubscriptionType.none_pending_in, // Table 3. SubscriptionType.none, // Table 4. SubscriptionType.none_pending_in, // Table 5. SubscriptionType.none_pending_in, // Table 6. SubscriptionType.none_pending_out_in, // Table 7. SubscriptionType.none_pending_in // Table 8. ), none_pending_out_in(SubscriptionType.from_pending_out, // Table 1. SubscriptionType.none_pending_out, // Table 2. SubscriptionType.none_pending_out_in, // Table 3. SubscriptionType.none_pending_out, // Table 4. SubscriptionType.to_pending_in, // Table 5. SubscriptionType.none_pending_in, // Table 6. SubscriptionType.none_pending_out_in, // Table 7. SubscriptionType.none_pending_in // Table 8. ), to(SubscriptionType.to, // Table 1. SubscriptionType.to, // Table 2. SubscriptionType.to_pending_in, // Table 3. SubscriptionType.to, // Table 4. SubscriptionType.to, // Table 5. SubscriptionType.none, // Table 6. SubscriptionType.to, // Table 7. SubscriptionType.none // Table 8. ), to_pending_in(SubscriptionType.both, // Table 1. SubscriptionType.to, // Table 2. SubscriptionType.to_pending_in, // Table 3. SubscriptionType.to, // Table 4. SubscriptionType.to_pending_in, // Table 5. SubscriptionType.none_pending_in, // Table 6. SubscriptionType.to_pending_in, // Table 7. SubscriptionType.none_pending_in // Table 8. ), from(SubscriptionType.from, // Table 1. SubscriptionType.none, // Table 2. SubscriptionType.from, // Table 3. SubscriptionType.none, // Table 4. SubscriptionType.from, // Table 5. SubscriptionType.from, // Table 6. SubscriptionType.from_pending_out, // Table 7. SubscriptionType.from // Table 8. ), from_pending_out(SubscriptionType.from_pending_out, // Table 1. SubscriptionType.none_pending_out, // Table 2. SubscriptionType.from_pending_out, // Table 3. SubscriptionType.none_pending_out, // Table 4. SubscriptionType.both, // Table 5. SubscriptionType.from, // Table 6. SubscriptionType.from_pending_out, // Table 7. SubscriptionType.from // Table 8. ), both(SubscriptionType.both, // Table 1. SubscriptionType.to, // Table 2. SubscriptionType.both, // Table 3. SubscriptionType.to, // Table 4. SubscriptionType.both, // Table 5. SubscriptionType.from, // Table 6. SubscriptionType.both, // Table 7. SubscriptionType.from // Table 8. ); private EnumMap<PresenceType, SubscriptionType> stateTransition = new EnumMap<PresenceType, SubscriptionType>(PresenceType.class); // ~--- constructors ------------------------------------------------------- private StateTransition(SubscriptionType out_subscribed, SubscriptionType out_unsubscribed, SubscriptionType in_subscribe, SubscriptionType in_unsubscribe, SubscriptionType in_subscribed, SubscriptionType in_unsubscribed, SubscriptionType out_subscribe, SubscriptionType out_unsubscribe) { stateTransition.put(PresenceType.out_subscribed, out_subscribed); stateTransition.put(PresenceType.out_unsubscribed, out_unsubscribed); stateTransition.put(PresenceType.in_subscribe, in_subscribe); stateTransition.put(PresenceType.in_unsubscribe, in_unsubscribe); stateTransition.put(PresenceType.in_subscribed, in_subscribed); stateTransition.put(PresenceType.in_unsubscribed, in_unsubscribed); stateTransition.put(PresenceType.out_subscribe, out_subscribe); stateTransition.put(PresenceType.out_unsubscribe, out_unsubscribe); } // ~--- get methods -------------------------------------------------------- /** * Method description * * * @param pres_type * * @return */ public SubscriptionType getStateTransition(PresenceType pres_type) { SubscriptionType res = stateTransition.get(pres_type); if (log.isLoggable(Level.FINEST)) { log.finest("this=" + this.toString() + ", pres_type=" + pres_type + ", res=" + res); } return res; } } /** * Enum description * */ public enum SubscriptionType { none("none", null), none_pending_out("none", "subscribe"), none_pending_in("none", null), none_pending_out_in("none", "subscribe"), to("to", null), to_pending_in( "to", null), from("from", null), from_pending_out("from", "subscribe"), both( "both", null), remove("remove", null); private Map<String, String> attrs = new LinkedHashMap<String, String>(2, 1.0f); // ~--- constructors ------------------------------------------------------- private SubscriptionType(String subscr, String ask) { attrs.put("subscription", subscr); if (ask != null) { attrs.put("ask", ask); } // end of if (ask != null) } // ~--- get methods -------------------------------------------------------- /** * Method description * * * @return */ public Map<String, String> getSubscriptionAttr() { return attrs; } } // ~--- static fields -------------------------------------------------------- /** * Private logger for class instances. */ private static Logger log = Logger.getLogger(RosterAbstract.class.getName()); public static final String CLIENT_XMLNS = "jabber:client"; /** Field description */ public static final String ROSTER_XMLNS = "jabber:iq:roster"; /** Field description */ public static final String ROSTER = "roster"; /** Field description */ public static final String GROUPS = "groups"; /** Field description */ public static final String GROUP = "group"; /** Field description */ public static final String NAME = "name"; /** Field description */ public static final String SUBSCRIPTION = "subscription"; /** Field description */ public static final String ROSTERHASH = "rosterhash"; /** Field description */ public static final String XMLNS = "jabber:iq:roster"; /** Field description */ public static final String XMLNS_DYNAMIC = "jabber:iq:roster-dynamic"; public static final String VER_ATT = "ver"; /** Field description */ public static final Element[] DISCO_FEATURES = { new Element("feature", new String[] { "var" }, new String[] { XMLNS }), new Element("feature", new String[] { "var" }, new String[] { XMLNS_DYNAMIC }) }; /** Field description */ public static final Element[] FEATURES = { new Element("ver", new String[] { "xmlns" }, new String[] { "urn:xmpp:features:rosterver" }) }; /** Field description */ public static final EnumSet<SubscriptionType> SUB_NONE = EnumSet.of( SubscriptionType.none, SubscriptionType.none_pending_out, SubscriptionType.none_pending_in, SubscriptionType.none_pending_out_in); /** Field description */ public static final EnumSet<SubscriptionType> SUB_TO = EnumSet.of(SubscriptionType.to, SubscriptionType.to_pending_in); /** Field description */ public static final EnumSet<SubscriptionType> SUB_FROM = EnumSet.of( SubscriptionType.from, SubscriptionType.from_pending_out); /** Field description */ public static final EnumSet<SubscriptionType> SUB_BOTH = EnumSet .of(SubscriptionType.both); /** Field description */ public static final EnumSet<SubscriptionType> TO_SUBSCRIBED = EnumSet.of( SubscriptionType.to, SubscriptionType.to_pending_in, SubscriptionType.both); /** Field description */ public static final EnumSet<SubscriptionType> FROM_SUBSCRIBED = EnumSet.of( SubscriptionType.from, SubscriptionType.from_pending_out, SubscriptionType.both); /** Field description */ public static final EnumSet<StanzaType> INITIAL_PRESENCES = EnumSet.of( StanzaType.available, StanzaType.unavailable); /** Field description */ public static final EnumSet<SubscriptionType> PENDING_IN = EnumSet.of( SubscriptionType.none_pending_in, SubscriptionType.none_pending_out_in, SubscriptionType.to_pending_in); /** Field description */ public static final EnumSet<SubscriptionType> PENDING_OUT = EnumSet.of( SubscriptionType.none_pending_out, SubscriptionType.none_pending_out_in, SubscriptionType.from_pending_out); private static EnumMap<SubscriptionType, StateTransition> subsToStateMap = new EnumMap<SubscriptionType, StateTransition>(SubscriptionType.class); // ~--- constant enums ------------------------------------------------------- /** * Enum description * */ public enum PresenceType { out_initial, out_subscribe, out_unsubscribe, out_subscribed, out_unsubscribed, out_probe, in_initial, in_subscribe, in_unsubscribe, in_subscribed, in_unsubscribed, in_probe, error; } // ~--- static initializers -------------------------------------------------- static { subsToStateMap.put(SubscriptionType.none, StateTransition.none); subsToStateMap.put(SubscriptionType.none_pending_out, StateTransition.none_pending_out); subsToStateMap.put(SubscriptionType.none_pending_in, StateTransition.none_pending_in); subsToStateMap.put(SubscriptionType.none_pending_out_in, StateTransition.none_pending_out_in); subsToStateMap.put(SubscriptionType.to, StateTransition.to); subsToStateMap.put(SubscriptionType.to_pending_in, StateTransition.to_pending_in); subsToStateMap.put(SubscriptionType.from, StateTransition.from); subsToStateMap.put(SubscriptionType.from_pending_out, StateTransition.from_pending_out); subsToStateMap.put(SubscriptionType.both, StateTransition.both); } // ~--- methods -------------------------------------------------------------- /** * Method description * * * @param session * @param jid * @param name * @param groups * @param otherData * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract void addBuddy(XMPPResourceConnection session, JID jid, String name, String[] groups, String otherData) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param buddy * @param groups * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract boolean addBuddyGroup(final XMPPResourceConnection session, JID buddy, final String[] groups) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param buddy * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract boolean containsBuddy(final XMPPResourceConnection session, JID buddy) throws NotAuthorizedException, TigaseDBException; // ~--- get methods ---------------------------------------------------------- // public abstract String[] getBuddies(final XMPPResourceConnection session, // boolean onlineOnly) // throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract JID[] getBuddies(final XMPPResourceConnection session) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param buddy * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract String[] getBuddyGroups(final XMPPResourceConnection session, JID buddy) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param buddy * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract String getBuddyName(final XMPPResourceConnection session, JID buddy) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param buddy * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract SubscriptionType getBuddySubscription( final XMPPResourceConnection session, JID buddy) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param jid * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract boolean isOnline(XMPPResourceConnection session, JID jid) throws NotAuthorizedException, TigaseDBException; // ~--- methods -------------------------------------------------------------- /** * Method description * * * @param session * @param jid * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract boolean presenceSent(XMPPResourceConnection session, JID jid) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param jid * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract boolean removeBuddy(final XMPPResourceConnection session, JID jid) throws NotAuthorizedException, TigaseDBException; // ~--- set methods ---------------------------------------------------------- /** * Method description * * * @param session * @param buddy * @param name * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract void setBuddyName(final XMPPResourceConnection session, JID buddy, final String name) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param subscription * @param buddy * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract void setBuddySubscription(final XMPPResourceConnection session, final SubscriptionType subscription, JID buddy) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param jid * @param online * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract void setOnline(XMPPResourceConnection session, JID jid, boolean online) throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param session * @param jid * @param sent * * @throws NotAuthorizedException * @throws TigaseDBException */ public abstract void setPresenceSent(XMPPResourceConnection session, JID jid, boolean sent) throws NotAuthorizedException, TigaseDBException; public abstract RosterElementIfc getRosterElement(XMPPResourceConnection session, JID jid) throws NotAuthorizedException, TigaseDBException; // ~--- get methods ---------------------------------------------------------- // public String[] getBuddies(final XMPPResourceConnection session, // final EnumSet<SubscriptionType> subscrs, boolean onlineOnly) /** * Method description * * * @param session * @param subscrs * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public JID[] getBuddies(final XMPPResourceConnection session, final EnumSet<SubscriptionType> subscrs) throws NotAuthorizedException, TigaseDBException { // final String[] allBuddies = getBuddies(session, onlineOnly); JID[] allBuddies = getBuddies(session); if (allBuddies == null) { return null; } // end of if (allBuddies == null) ArrayList<JID> list = new ArrayList<JID>(); for (JID buddy : allBuddies) { final SubscriptionType subs = getBuddySubscription(session, buddy); if (subscrs.contains(subs)) { list.add(buddy); } // end of if (subscrs.contains(subs)) } // end of for () return list.toArray(new JID[list.size()]); } /** * Method description * * * @param session * * @return */ public String getBuddiesHash(final XMPPResourceConnection session) { String hash = (String) session.getCommonSessionData(ROSTERHASH); return (hash != null ? hash : ""); } /** * Method description * * * @param session * @param buddy * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public Element getBuddyItem(final XMPPResourceConnection session, JID buddy) throws NotAuthorizedException, TigaseDBException { SubscriptionType subscr = getBuddySubscription(session, buddy); if (subscr == null) { subscr = SubscriptionType.none; setBuddySubscription(session, subscr, buddy); } // end of if Element item = new Element("item"); item.setAttribute("jid", buddy.toString()); item.addAttributes(subscr.getSubscriptionAttr()); String name = getBuddyName(session, buddy); if (name != null) { item.setAttribute("name", XMLUtils.escape(name)); } String[] groups = getBuddyGroups(session, buddy); if (groups != null) { for (String gr : groups) { Element group = new Element("group"); group.setCData(XMLUtils.escape(gr)); item.addChild(group); } // end of for () } // end of if-else return item; } /** * Method description * * * @param session * @param packet * * @return * * @throws NotAuthorizedException */ public PresenceType getPresenceType(final XMPPResourceConnection session, final Packet packet) throws NotAuthorizedException { BareJID to = (packet.getStanzaTo() != null) ? packet.getStanzaTo().getBareJID() : null; StanzaType type = packet.getType(); if (type == null) { type = StanzaType.available; } else { if (type == StanzaType.error) { return PresenceType.error; } } if ((to == null) || !session.isUserId(to)) { if (INITIAL_PRESENCES.contains(type)) { return PresenceType.out_initial; } if (type == StanzaType.subscribe) { return PresenceType.out_subscribe; } // end of if (type == StanzaType.subscribe) if (type == StanzaType.unsubscribe) { return PresenceType.out_unsubscribe; } // end of if (type == StanzaType.unsubscribe) if (type == StanzaType.subscribed) { return PresenceType.out_subscribed; } // end of if (type == StanzaType.subscribed) if (type == StanzaType.unsubscribed) { return PresenceType.out_unsubscribed; } // end of if (type == StanzaType.unsubscribed) // StanzaType.probe is invalid here.... // if (type == StanzaType.probe) { // return PresenceType.out_probe; // } // if (type == StanzaType.probe) } // end of if (to == null || to.equals(session.getUserId())) if ((to != null) && session.isUserId(to)) { if (INITIAL_PRESENCES.contains(type)) { return PresenceType.in_initial; } if (type == StanzaType.subscribe) { return PresenceType.in_subscribe; } // end of if (type == StanzaType.subscribe) if (type == StanzaType.unsubscribe) { return PresenceType.in_unsubscribe; } // end of if (type == StanzaType.unsubscribe) if (type == StanzaType.subscribed) { return PresenceType.in_subscribed; } // end of if (type == StanzaType.subscribed) if (type == StanzaType.unsubscribed) { return PresenceType.in_unsubscribed; } // end of if (type == StanzaType.unsubscribed) if (type == StanzaType.probe) { return PresenceType.in_probe; } // end of if (type == StanzaType.probe) } // end of if (to != null && !to.equals(session.getUserId())) return null; } // public List<Element> getRosterItems(XMPPResourceConnection session, boolean // online) /** * Method description * * * @param session * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public List<Element> getRosterItems(XMPPResourceConnection session) throws NotAuthorizedException, TigaseDBException { LinkedList<Element> items = new LinkedList<Element>(); // String[] buddies = getBuddies(session, online); JID[] buddies = getBuddies(session); if (buddies != null) { for (JID buddy : buddies) { Element buddy_item = getBuddyItem(session, buddy); // String item_group = buddy_item.getCData("/item/group"); items.add(buddy_item); } } return items; } /** * Method description * * * @param subscription * @param presence * * @return */ public SubscriptionType getStateTransition(final SubscriptionType subscription, final PresenceType presence) { return subsToStateMap.get(subscription).getStateTransition(presence); } // ~--- methods -------------------------------------------------------------- /** * Method description * * * @param buddy * * @return */ public String groupNode(JID buddy) { return ROSTER + "/" + buddy.getBareJID(); } // public abstract void setBuddyOnline(final XMPPResourceConnection session, // final String buddy, final boolean online) // throws NotAuthorizedException, TigaseDBException; // // public abstract boolean isBuddyOnline(final XMPPResourceConnection session, // final String buddy) // throws NotAuthorizedException, TigaseDBException; // public abstract void setBuddyGroups(XMPPResourceConnection session, // String buddy, String[] groups) // throws NotAuthorizedException, TigaseDBException; /** * Method description * * * @param repo * * @throws TigaseDBException * @throws TigaseDBException */ public void init(UserRepository repo) throws TigaseDBException, TigaseDBException { } // ~--- get methods ---------------------------------------------------------- /** * Method description * * * @param session * @param jid * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public boolean isPendingIn(final XMPPResourceConnection session, JID jid) throws NotAuthorizedException, TigaseDBException { SubscriptionType subscr = getBuddySubscription(session, jid); return PENDING_IN.contains(subscr); } /** * Method description * * * @param session * @param jid * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public boolean isSubscribedFrom(final XMPPResourceConnection session, JID jid) throws NotAuthorizedException, TigaseDBException { SubscriptionType subscr = getBuddySubscription(session, jid); return FROM_SUBSCRIBED.contains(subscr); } /** * Method description * * * @param subscr * * @return */ public boolean isSubscribedFrom(SubscriptionType subscr) { return FROM_SUBSCRIBED.contains(subscr); } /** * Method description * * * @param session * @param jid * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public boolean isSubscribedTo(final XMPPResourceConnection session, JID jid) throws NotAuthorizedException, TigaseDBException { SubscriptionType subscr = getBuddySubscription(session, jid); return TO_SUBSCRIBED.contains(subscr); } // ~--- methods -------------------------------------------------------------- /** * Method description * * * @param session * @param results * @param item * * @throws NotAuthorizedException * @throws TigaseDBException * @throws NoConnectionIdException */ public void updateBuddyChange(final XMPPResourceConnection session, final Queue<Packet> results, final Element item) throws NotAuthorizedException, TigaseDBException, NoConnectionIdException { Element update = new Element("iq"); update.setXMLNS(CLIENT_XMLNS); update.setAttribute("type", StanzaType.set.toString()); Element query = new Element("query"); query.setXMLNS(ROSTER_XMLNS); query.addAttribute(VER_ATT, getBuddiesHash(session)); query.addChild(item); update.addChild(query); for (XMPPResourceConnection conn : session.getActiveSessions()) { Element conn_update = update.clone(); conn_update.setAttribute("to", conn.getBareJID().toString()); conn_update.setAttribute("id", "rst" + session.nextStanzaId()); Packet pack_update = Packet.packetInstance(conn_update, null, conn.getJID()); pack_update.setPacketTo(conn.getConnectionId()); // pack_update.setPacketFrom(session.getJID()); results.offer(pack_update); } // end of for (XMPPResourceConnection conn: sessions) } /** * Method description * * * @param session * @param presence * @param jid * * @return * * @throws NotAuthorizedException * @throws TigaseDBException */ public boolean updateBuddySubscription(final XMPPResourceConnection session, final PresenceType presence, JID jid) throws NotAuthorizedException, TigaseDBException { SubscriptionType current_subscription = getBuddySubscription(session, jid); if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "current_subscription={0} for jid={1}", new Object[] { current_subscription, jid }); } if (current_subscription == null) { // don't create new roster item for incomming unsubscribe presence #219 / // #210 if (presence != PresenceType.in_unsubscribe && presence != PresenceType.out_unsubscribe) { addBuddy(session, jid, null, null, null); } current_subscription = SubscriptionType.none; } final SubscriptionType new_subscription = getStateTransition(current_subscription, presence); if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "new_subscription={0} for presence={1}", new Object[] { new_subscription, presence }); } if ((current_subscription == SubscriptionType.none_pending_in) && (presence == PresenceType.out_unsubscribed)) { removeBuddy(session, jid); return false; } if (current_subscription != new_subscription) { setBuddySubscription(session, new_subscription, jid); return true; } else { return false; } // if ((SUB_NONE.contains(current_subscription) // && SUB_NONE.contains(new_subscription)) // || (SUB_TO.contains(current_subscription) // && SUB_TO.contains(new_subscription)) // || (SUB_FROM.contains(current_subscription) // && SUB_FROM.contains(new_subscription)) // || (SUB_BOTH.contains(current_subscription) // && SUB_BOTH.contains(new_subscription))) { // return false; // } else { // setBuddySubscription(session, new_subscription, jid); // return true; // } } /** * Method description * * * @param roster_str * @param session */ public void updateRosterHash(String roster_str, XMPPResourceConnection session) { String roster_hash = null; try { roster_hash = Algorithms.hexDigest("", roster_str, "MD5"); } catch (Exception e) { roster_hash = null; } session.putCommonSessionData(ROSTERHASH, roster_hash); } public abstract void logout(XMPPResourceConnection session); /** * @param session * @param buddy * @return * @throws TigaseDBException * @throws NotAuthorizedException */ public abstract String getCustomStatus(XMPPResourceConnection session, JID buddy) throws NotAuthorizedException, TigaseDBException; }