From b2127ce982158c115f4fdc66c007a8d7c15b6189 Mon Sep 17 00:00:00 2001 From: Matt Tucker Date: Mon, 10 Feb 2003 05:01:01 +0000 Subject: [PATCH] Initial roster support. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@1835 b35dd754-fafc-0310-a699-88a17e54d16e --- .../org/jivesoftware/smack/PacketReader.java | 59 ++++- source/org/jivesoftware/smack/Roster.java | 123 +++++++++- .../org/jivesoftware/smack/RosterEntry.java | 93 +++++++ .../org/jivesoftware/smack/RosterGroup.java | 167 +++++++++++++ .../jivesoftware/smack/XMPPConnection.java | 29 ++- .../jivesoftware/smack/packet/Presence.java | 4 +- .../smack/packet/RosterPacket.java | 232 ++++++++++++++++++ 7 files changed, 687 insertions(+), 20 deletions(-) create mode 100644 source/org/jivesoftware/smack/RosterEntry.java create mode 100644 source/org/jivesoftware/smack/RosterGroup.java create mode 100644 source/org/jivesoftware/smack/packet/RosterPacket.java diff --git a/source/org/jivesoftware/smack/PacketReader.java b/source/org/jivesoftware/smack/PacketReader.java index dd3946a44..59c68a283 100644 --- a/source/org/jivesoftware/smack/PacketReader.java +++ b/source/org/jivesoftware/smack/PacketReader.java @@ -72,6 +72,9 @@ import org.jivesoftware.smack.util.StringUtils; */ class PacketReader { + /** + * Namespace used to store packet properties. + */ private static final String PROPERTIES_NAMESPACE = "http://www.jivesoftware.com/xmlns/xmpp/properties"; @@ -107,8 +110,21 @@ class PacketReader { listenerThread.setDaemon(true); try { - XmlPullParserFactory factory = XmlPullParserFactory.newInstance( - System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); + final String defaultProviderName = "org.xmlpull.mxp1.MXParserFactory"; + XmlPullParserFactory factory = null; + try { + // Attempt to load a factory implementation using a system property + // and a classloader context. + factory = XmlPullParserFactory.newInstance( + System.getProperty(XmlPullParserFactory.PROPERTY_NAME), + Thread.currentThread().getContextClassLoader().getClass()); + } + catch (Exception e) { + if (factory == null) { + // Loading failed. Therefore, use the hardcoded default. + factory = XmlPullParserFactory.newInstance(defaultProviderName, null); + } + } factory.setNamespaceAware(true); parser = factory.newPullParser(); parser.setInput(connection.reader); @@ -323,6 +339,9 @@ class PacketReader { if (namespace.equals("jabber:iq:auth")) { iqPacket = parseAuthentication(parser); } + else if (namespace.equals("jabber:iq:roster")) { + iqPacket = parseRoster(parser); + } } else if (parser.getName().equals("error")) { error = parseError(parser); @@ -387,6 +406,37 @@ class PacketReader { return authentication; } + private static RosterPacket parseRoster(XmlPullParser parser) throws Exception { + RosterPacket roster = new RosterPacket(); + boolean done = false; + RosterPacket.Item item = null; + while (!done) { + int eventType = parser.next(); + if (eventType == parser.START_TAG) { + if (parser.getName().equals("item")) { + String jid = parser.getAttributeValue("", "jid"); + String name = parser.getAttributeValue("", "name"); + String subscription = parser.getAttributeValue("", "subscription"); + item = new RosterPacket.Item(jid, name); + item.setItemType(RosterPacket.ItemType.fromString(subscription)); + } + if (parser.getName().equals("group")) { + String groupName = parser.nextText(); + item.addGroupName(groupName); + } + } + else if (eventType == parser.END_TAG) { + if (parser.getName().equals("item")) { + roster.addRosterItem(item); + } + if (parser.getName().equals("query")) { + done = true; + } + } + } + return roster; + } + private static Error parseError(XmlPullParser parser) throws Exception { String errorCode = null; for (int i=0; inull if the + * group doesn't exist. + * + * @param name the name of the group. + * @return the roster group with the specified name. + */ + public RosterGroup getGroup(String name) { + synchronized (groups) { + return (RosterGroup)groups.get(name); + } + } + + /** + * Returns an iterator the for all the roster groups. + * + * @return an iterator for all roster groups. + */ + public Iterator getGroups() { + synchronized (groups) { + List groupsList = Collections.unmodifiableList(new ArrayList(groups.values())); + return groupsList.iterator(); + } + } + + /** + * Listens for all roster packets and processes them. + */ + private class RosterListener implements PacketListener { + + public void processPacket(Packet packet) { + RosterPacket rosterPacket = (RosterPacket)packet; + for (Iterator i=rosterPacket.getRosterItems(); i.hasNext(); ) { + RosterPacket.Item item = (RosterPacket.Item)i.next(); + RosterEntry entry = new RosterEntry(item.getUser(), item.getName(), + connection); + // Find the list of groups that the user currently belongs to. + List currentGroupNames = new ArrayList(); + for (Iterator j = entry.getGroups(); j.hasNext(); ) { + RosterGroup group = (RosterGroup)j.next(); + currentGroupNames.add(group.getName()); + } + + List newGroupNames = new ArrayList(); + for (Iterator k = item.getGroupNames(); k.hasNext(); ) { + String groupName = (String)k.next(); + // Add the group name to the list. + newGroupNames.add(groupName); + + // Add the entry to the group. + RosterGroup group = getGroup(groupName); + if (group == null) { + group = createGroup(groupName); + groups.put(groupName, group); + } + // Add the entry. + group.addEntryLocal(entry); + } + + // We have the list of old and new group names. We now need to + // remove the entry from the all the groups it may no longer belong + // to. We do this by subracting the new group set from the old. + for (int m=0; m 0) { String str = new String(cbuf, off, count); - receivedText1.append(str); - receivedText2.append(str); - if (str.endsWith(">")) { + int index = str.lastIndexOf(">"); + if (index != -1) { + receivedText1.append(str.substring(0, index+1)); + receivedText2.append(str.substring(0, index+1)); receivedText1.append(NEWLINE); receivedText2.append(NEWLINE); + if (str.length() > index) { + receivedText1.append(str.substring(index+1)); + receivedText2.append(str.substring(index+1)); + } + } + else { + receivedText1.append(str); + receivedText2.append(str); } } return count; diff --git a/source/org/jivesoftware/smack/packet/Presence.java b/source/org/jivesoftware/smack/packet/Presence.java index 33d0c9cec..6407f4d8c 100644 --- a/source/org/jivesoftware/smack/packet/Presence.java +++ b/source/org/jivesoftware/smack/packet/Presence.java @@ -52,8 +52,6 @@ package org.jivesoftware.smack.packet; -import org.jivesoftware.smack.*; - /** * Represents XMPP presence packets. Every presence packet has a type, which is one of * the following values: @@ -84,7 +82,7 @@ import org.jivesoftware.smack.*; * the clients current presence status. Second, they are used to subscribe and * unsubscribe users from the roster. * - * @see Roster + * @see RosterPacket * @author Matt Tucker */ public class Presence extends Packet { diff --git a/source/org/jivesoftware/smack/packet/RosterPacket.java b/source/org/jivesoftware/smack/packet/RosterPacket.java new file mode 100644 index 000000000..f357e5259 --- /dev/null +++ b/source/org/jivesoftware/smack/packet/RosterPacket.java @@ -0,0 +1,232 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright (C) 2002-2003 Jive Software. All rights reserved. + * ==================================================================== + * The Jive Software License (based on Apache Software License, Version 1.1) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by + * Jive Software (http://www.jivesoftware.com)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Smack" and "Jive Software" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please + * contact webmaster@jivesoftware.com. + * + * 5. Products derived from this software may not be called "Smack", + * nor may "Smack" appear in their name, without prior written + * permission of Jive Software. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + */ + +package org.jivesoftware.smack.packet; + +import java.util.*; + +/** + * Represents XMPP roster packets. + * + * @author Matt Tucker + */ +public class RosterPacket extends IQ { + + private List rosterItems = new ArrayList(); + + public void addRosterItem(Item entry) { + synchronized (rosterItems) { + rosterItems.add(entry); + } + } + + public Iterator getRosterItems() { + synchronized (rosterItems) { + List entries = Collections.unmodifiableList(new ArrayList(rosterItems)); + return entries.iterator(); + } + } + + public String getQueryXML() { + StringBuffer buf = new StringBuffer(); + buf.append(""); + synchronized (rosterItems) { + for (int i=0; i"); + return buf.toString(); + } + + public static class Item { + + private String user; + private String name; + private ItemType itemType; + private List groupNames; + + public Item(String user, String name) { + this.user = user; + this.name = name; + itemType = null; + groupNames = new ArrayList(); + } + + public String getUser() { + return user; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ItemType getItemType() { + return itemType; + } + + public void setItemType(ItemType itemType) { + this.itemType = itemType; + } + + public Iterator getGroupNames() { + synchronized (groupNames) { + return Collections.unmodifiableList(groupNames).iterator(); + } + } + + public void addGroupName(String groupName) { + synchronized (groupNames) { + if (!groupNames.contains(groupName)) { + groupNames.add(groupName); + } + } + } + + public void removeGroupName(String groupName) { + synchronized (groupNames) { + groupNames.remove(groupName); + } + } + + public String toXML() { + StringBuffer buf = new StringBuffer(); + buf.append(""); + synchronized (groupNames) { + for (int i=0; i").append(groupName).append(""); + } + } + buf.append(""); + return buf.toString(); + } + } + + public static class ItemStatus { + + public static final ItemStatus SUBSCRIBED = new ItemStatus("subscribed"); + public static final ItemStatus SUBSCRIPTION_PENDING = new ItemStatus("subscribe"); + public static final ItemStatus UNSUBCRIPTION_PENDING = new ItemStatus("unsubscribe"); + + public static ItemStatus fromString(String value) { + if ("subscribed".equals(value)) { + return SUBSCRIBED; + } + else if ("subscribe".equals(value)) { + return SUBSCRIPTION_PENDING; + } + else { + return SUBSCRIBED; + } + } + + private String value; + + private ItemStatus (String value) { + this.value = value; + } + + public String toString() { + return value; + } + + } + + public static class ItemType { + + public static final ItemType NONE = new ItemType("none"); + public static final ItemType TO = new ItemType("to"); + public static final ItemType FROM = new ItemType("from"); + public static final ItemType BOTH = new ItemType("both"); + + public static ItemType fromString(String value) { + if ("none".equals(value)) { + return NONE; + } + else if ("to".equals(value)) { + return TO; + } + else if ("from".equals(value)) { + return FROM; + } + else if ("both".equals(value)) { + return BOTH; + } + else { + return null; + } + } + + private String value; + + public ItemType (String value) { + this.value = value; + } + + public String toString() { + return value; + } + } +}