diff --git a/build/lib/xpp.jar b/build/lib/xpp.jar new file mode 100644 index 000000000..7418dbf94 Binary files /dev/null and b/build/lib/xpp.jar differ diff --git a/source/org/jivesoftware/smack/Chat.java b/source/org/jivesoftware/smack/Chat.java new file mode 100644 index 000000000..7e8128739 --- /dev/null +++ b/source/org/jivesoftware/smack/Chat.java @@ -0,0 +1,230 @@ +/** + * $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@coolservlets.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; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.filter.ThreadFilter; + +/** + * A chat is a series of messages sent between two users. Each chat has + * a unique ID, which is used to track which messages are part of the chat. + * + * @see XMPPConnection#createChat(String) + * @author Matt Tucker + */ +public class Chat { + + /** + * A prefix helps to make sure that ID's are unique across mutliple instances. + */ + private static String prefix = StringUtils.randomString(3); + + /** + * Keeps track of the current increment, which is appended to the prefix to + * forum a unique ID. + */ + private static long id = 0; + + /** + * Returns the next unique id. Each id made up of a short alphanumeric + * prefix along with a unique numeric value. + * + * @return the next id. + */ + private static synchronized String nextID() { + return prefix + Long.toString(id++); + } + + private XMPPConnection connection; + private String chatID; + private String participant; + private PacketCollector messageCollector; + + /** + * Creates a new chat with the specified user. + * + * @param connection the connection the chat will use. + * @param participant the user to chat with. + */ + public Chat(XMPPConnection connection, String participant) { + this.connection = connection; + this.participant = participant; + // Automatically assign the next chat ID. + chatID = nextID(); + + messageCollector = connection.getPacketReader().createPacketCollector( + new ThreadFilter(chatID)); + } + + /** + * Creates a new chat with the specified user and chat ID (the XMPP "thread). + * + * @param connection the connection the chat will use. + * @param participant the user to chat with. + * @param chatID the chat ID to use. + */ + public Chat(XMPPConnection connection, String participant, String chatID) { + this.connection = connection; + this.participant = participant; + this.chatID = chatID; + + messageCollector = connection.getPacketReader().createPacketCollector( + new ThreadFilter(chatID)); + } + + /** + * Returns the unique id of this chat, which corresponds to the + * thread field of XMPP messages. + * + * @return the unique ID of this chat. + */ + public String getChatID() { + return chatID; + } + + /** + * Returns the name of the user the chat is with. + * + * @return the name of the user the chat is occuring with. + */ + public String getParticipant() { + return participant; + } + + /** + * Sends the specified text as a message to the other chat participant. + * This is a convenience method for: + * + *
+ * Message message = chat.createMessage(); + * message.setBody(messageText); + * chat.sendMessage(message); + *+ * + * @param text the text to send. + * @throws XMPPException if sending the message fails. + */ + public void sendMessage(String text) throws XMPPException { + Message message = createMessage(); + message.setBody(text); + connection.getPacketWriter().sendPacket(message); + } + + /** + * Creates a new Message to the chat participant. The message returned + * will have its thread property set with this chat ID. + * + * @return a new message addressed to the chat participant and + * using the correct thread value. + * @see #sendMessage(Message) + */ + public Message createMessage() { + Message message = new Message(participant, Message.CHAT); + message.setThread(chatID); + return message; + } + + /** + * Sends a message to the other chat participant. The thread property of + * the message will automatically set to this chat ID in case the Message + * was not created using the {@link #createMessage() createMessage} method. + * + * @param message + * @throws XMPPException + */ + public void sendMessage(Message message) throws XMPPException { + // Force the chatID since the user elected to send the message + // through this chat object. + message.setThread(chatID); + connection.getPacketWriter().sendPacket(message); + } + + /** + * Polls for and returns the next message, or null if there isn't + * a message immediately available. This method provides significantly different + * functionalty than the {@link #nextMessage()} method since it's non-blocking. + * In other words, the method call will always return immediately, whereas the + * nextMessage method will return only when a message is available (or after + * a specific timeout). + * + * @return the next message if one is immediately available and + * null otherwise. + */ + public Message pollMessage() { + return (Message)messageCollector.pollResult(); + } + + /** + * Returns the next available message in the chat. The method will block + * indefinitely (won't return) until a message is available. + * + * @return the next message. + */ + public Message nextMessage() { + return (Message)messageCollector.nextResult(); + } + + /** + * Returns the next available message in the chat. The method will block + * for up to the timeout. If a message still isn't available then, null + * will be returned. + * + * @param timeout the maximum amount of time to wait for the next message. + * @return the next message, or null if the timeout elapses without a + * message becoming available. + */ + public Message nextMessage(long timeout) { + return (Message)messageCollector.nextResult(timeout); + } +} diff --git a/source/org/jivesoftware/smack/GroupChat.java b/source/org/jivesoftware/smack/GroupChat.java new file mode 100644 index 000000000..da77914a9 --- /dev/null +++ b/source/org/jivesoftware/smack/GroupChat.java @@ -0,0 +1,189 @@ +/** + * $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@coolservlets.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; + +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.filter.*; + +/** + * A GroupChat is a conversation that takes plaaces among many users in a virtual + * room. When joining a group chat, you specify a nickname, which is the identity + * that other chat room users see. + * + * @see XMPPConnection#createGroupChat(String) + * @author Matt Tucker + */ +public class GroupChat { + + private XMPPConnection connection; + private String room; + private String nickname = null; + private boolean joined = false; + + private PacketCollector collector; + + /** + * Creates a new group chat with the specified connection and room name. + * + * @param connection + * @param room the name of the room in the form "roomName@service", where + * "service" is the hostname at which the multi-user chat + * service is running. + */ + public GroupChat(XMPPConnection connection, String room) { + this.connection = connection; + this.room = room; + collector = connection.getPacketReader().createPacketCollector( + new FromContainsFilter(room)); + } + + /** + * Joins the chat room using the specified nickname. If already joined as + * another nickname, will leave as that name first before joining under the new + * name. + * + * @param nickname the nicknam to use. + * @throws XMPPException if an error occurs joining the room. + */ + public synchronized void join(String nickname) throws XMPPException { + if (nickname == null || nickname.equals("")) { + throw new IllegalArgumentException("Nickname must not be null or blank."); + } + // If we've already joined the room, leave it before joining under a new + // nickname. + if (joined) { + leave(); + } + // We join a room by sending a presence packet where the "to" + // field is in the form "roomName@service/nickname" + Presence joinPresence = new Presence(true); + joinPresence.setTo(room + "/" + nickname); + connection.getPacketWriter().sendPacket(joinPresence); + // Wait for a presence packet back from the server. + PacketFilter responseFilter = new AndFilter( + new FromContainsFilter(room + "/" + nickname), + new PacketTypeFilter(Presence.class)); + PacketCollector response = connection.getPacketReader().createPacketCollector( + responseFilter); + // Wait up to five seconds for a reply. + Presence presence = (Presence)response.nextResult(5000); + if (presence == null) { + throw new XMPPException("No response from server."); + } + else if (presence.getError() != null) { + throw new XMPPException(presence.getError().getMessage()); + + } + this.nickname = nickname; + joined = true; + } + + /** + * Leave the chat room. + */ + public synchronized void leave() { + // If not joined already, do nothing. + if (!joined) { + return; + } + // We leave a room by sending a presence packet where the "to" + // field is in the form "roomName@service/nickname" + Presence leavePresence = new Presence(false); + leavePresence.setTo(room + "/" + nickname); + connection.getPacketWriter().sendPacket(leavePresence); + nickname = null; + joined = false; + } + + /** + * Returns the nickname that was used to join the room, or null if not + * currently joined. + * + * @return the nickname currently being used.. + */ + public String getNickname() { + return nickname; + } + + /** + * Sends a message to the chat room. + * + * @param text the text of the message to send. + * @throws XMPPException if sending the message fails. + */ + public void sendMessage(String text) throws XMPPException { + Message message = new Message(room, Message.CHAT); + message.setBody(text); + connection.getPacketWriter().sendPacket(message); + } + + /** + * Creates a new Message to send to the chat room. + * + * @return a new Message addressed to the chat room. + */ + public Message createMessage() { + return new Message(room, Message.CHAT); + } + + /** + * Sends a Message to the chat room. + * + * @param message the message. + * @throws XMPPException if sending the message fails. + */ + public void sendMessage(Message message) throws XMPPException { + connection.getPacketWriter().sendPacket(message); + } +} diff --git a/source/org/jivesoftware/smack/PacketCollector.java b/source/org/jivesoftware/smack/PacketCollector.java new file mode 100644 index 000000000..af2737eac --- /dev/null +++ b/source/org/jivesoftware/smack/PacketCollector.java @@ -0,0 +1,186 @@ +/** + * $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@coolservlets.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; + +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.filter.PacketFilter; + +import java.util.LinkedList; + +/** + * Provides a mechanism to collect packets into a result queue that pass a + * specified filter. The collector lets you perform blocking and polling + * operations on the result queue. So, a PacketCollector is more suitable to + * use than a {@link PacketListener} when you need to wait for a specific + * result. + * + * @see PacketReader#createPacketCollector(PacketFilter) + * @author Matt Tucker + */ +public class PacketCollector { + + private PacketFilter packetFilter; + private LinkedList resultQueue; + private PacketReader packetReader; + private boolean cancelled = false; + + /** + * Creates a new packet collector. If the packet filter is null, then + * all packets will match this collector. + * + * @param packetReader the packetReader the collector is tied to. + * @param packetFilter determines which packets will be returned by this collector. + */ + protected PacketCollector(PacketReader packetReader, PacketFilter packetFilter) { + this.packetReader = packetReader; + this.packetFilter = packetFilter; + this.resultQueue = new LinkedList(); + + // Add the collector to the packet reader's list of active collector. + synchronized (packetReader.collectors) { + packetReader.collectors.add(this); + } + } + + /** + * Explicitly cancels the packet collector so that no more results are + * queued up. Once a packet collector has been cancelled, it cannot be + * re-enabled. Instead, a new packet collector must be created. + */ + public void cancel() { + // If the packet collector has already been cancelled, do nothing. + if (cancelled) { + return; + } + else { + cancelled = true; + // Remove object from collectors list by setting the value in the + // list at the correct index to null. The collector thread will + // automatically remove the actual list entry when it can. + int index = packetReader.collectors.indexOf(this); + packetReader.collectors.set(index, null); + } + } + + /** + * Returns the packet filter associated with this packet collector. The packet + * filter is used to determine what packets are queued as results. + * + * @return the packet filter. + */ + public PacketFilter getPacketFilter() { + return packetFilter; + } + + /** + * Polls to see if a result is currently available and returns it, or + * immediately returns null if no packets are currently in the + * result queue. + * + * @return the next packet result, or null if there are no more + * results. + */ + public synchronized Packet pollResult() { + if (resultQueue.isEmpty()) { + return null; + } + else { + return (Packet)resultQueue.removeLast(); + } + } + + public synchronized Packet nextResult() { + // Wait indefinitely until there is a result to return. + while (resultQueue.isEmpty()) { + try { + wait(); + } + catch (InterruptedException ie) { } + } + return (Packet)resultQueue.removeLast(); + } + + public synchronized Packet nextResult(long timeout) { + // Wait up to the specified amount of time for a result. + if (resultQueue.isEmpty()) { + try { + wait(timeout); + } + catch (InterruptedException ie) { } + } + // If still no result, return null. + if (resultQueue.isEmpty()) { + return null; + } + else { + return (Packet)resultQueue.removeLast(); + } + } + + /** + * Processes a packet to see if it meets the criteria for this packet collector. + * If so, the packet is added to the result queue. + * + * @param packet the packet to process. + */ + protected synchronized void processPacket(Packet packet) { + if (packet == null) { + return; + } + if (packetFilter == null || packetFilter.accept(packet)) { + resultQueue.addFirst(packet); + // Notify waiting threads a result is available. + notifyAll(); + } + } +} diff --git a/source/org/jivesoftware/smack/PacketListener.java b/source/org/jivesoftware/smack/PacketListener.java new file mode 100644 index 000000000..a0329d434 --- /dev/null +++ b/source/org/jivesoftware/smack/PacketListener.java @@ -0,0 +1,77 @@ +/** + * $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@coolservlets.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; + +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.filter.PacketFilter; + +/** + * Provides a mechanism to listen for packets that pass a specified filter. + * This allows event-style programming -- every time a new packet is found, + * the {@link #processPacket(Packet)} method will be called. This is the + * opposite approach to the functionality provided by a {@link PacketCollector} + * which lets you block while waiting for results. + * + * @see PacketReader#addPacketListener(PacketListener, PacketFilter) + * @author Matt Tucker + */ +public interface PacketListener { + + /** + * Process the next packet sent to this packet listener. + * + * @param packet the packet to process. + */ + public void processPacket(Packet packet); + +} diff --git a/source/org/jivesoftware/smack/PacketReader.java b/source/org/jivesoftware/smack/PacketReader.java new file mode 100644 index 000000000..e8caece17 --- /dev/null +++ b/source/org/jivesoftware/smack/PacketReader.java @@ -0,0 +1,460 @@ +/** + * $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@coolservlets.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; + +import org.xmlpull.v1.*; +import java.util.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.filter.PacketFilter; + +/** + * Listens for XML traffic from the XMPP server, and parses it into packet objects. + * + * @see XMPPConnection#getPacketReader() + * @see PacketCollector + * @author Matt Tucker + */ +public class PacketReader { + + private Thread readerThread; + private Thread listenerThread; + + private XMPPConnection connection; + private XmlPullParser parser; + private boolean done = false; + protected List collectors = new ArrayList(); + private List listeners = Collections.synchronizedList(new ArrayList()); + + private String connectionID = null; + private Object connectionIDLock = new Object(); + + protected PacketReader(XMPPConnection connection) { + this.connection = connection; + + readerThread = new Thread() { + public void run() { + parsePackets(); + } + }; + readerThread.setName("Smack Packet Reader"); + readerThread.setDaemon(true); + + listenerThread = new Thread() { + public void run() { + processListeners(); + } + }; + listenerThread.setName("Smack Listener Processor"); + listenerThread.setDaemon(true); + + try { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance( + System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); + factory.setNamespaceAware(true); + parser = factory.newPullParser(); + parser.setInput(connection.reader); + + } + catch (XmlPullParserException xppe) { + xppe.printStackTrace(); + } + } + + public PacketCollector createPacketCollector(PacketFilter packetFilter) { + return new PacketCollector(this, packetFilter); + } + + public void addPacketListener(PacketListener packetListener, PacketFilter packetFilter) { + // TODO: implement + } + + public void removePacketListener(PacketListener packetListener) { + // TODO: implement + } + + /** + * Starts the packet reader thread and returns once a connection to the server + * has been established. A connection will be attempted for a maximum of five + * seconds. An XMPPException will be thrown if the connection fails. + * + * @throws XMPPException if the server fails to send an opening stream back + * for more than five seconds. + */ + public void startup() throws XMPPException { + readerThread.start(); + listenerThread.start(); + // Wait for stream tag before returing. We'll wait a maximum of five seconds before + // giving up and throwing an error. + try { + synchronized(connectionIDLock) { + connectionIDLock.wait(5000); + } + } + catch (InterruptedException ie) { } + if (connectionID == null) { + throw new XMPPException("Connection failed. No response from server."); + } + else { + connection.connectionID = connectionID; + } + } + + /** + * Shuts the packet reader down. + */ + public void shutdown() { + done = true; + } + + private void processListeners() { + boolean processedPacket = false; + while (true) { + synchronized(listeners) { + int size = listeners.size(); + for (int i=0; i
+ * // Create a connection to the jivesoftware.com XMPP server. + * XMPPConnection con = new XMPPConnection("jivesoftware.com"); + * // Most servers require you to login before performing other tasks. + * con.login("jsmith", "mypass"); + * // Start a new conversation with John Doe and send him a message. + * Chat chat = new Chat("jdoe@jabber.org"); + * chat.sendMessage("Hey, how's it going?"); + *+ * + * Every connection has a PacketReader and PacketWriter instance, which are used + * to read and write XML with the server. + * + * @author Matt Tucker + */ +public class XMPPConnection { + + private static final String NEWLINE = System.getProperty ("line.separator"); + + /** + * Value that indicates whether debugging is enabled. When enabled, a debug + * window will apear for each new connection that will contain the following + * information:
+ * + * @param username the username. + * @param password the password. + * @throws XMPPException if an error occurs. + */ + public synchronized void login(String username, String password) throws XMPPException { + if (!isConnected()) { + throw new IllegalStateException("Not connected to server."); + } + Authentication auth = new Authentication(username, password, "Smack"); + packetWriter.sendPacket(auth); + // Wait up to five seconds for a response from the server. + PacketCollector collector = packetReader.createPacketCollector( + new PacketIDFilter(auth.getPacketID())); + IQ response = (IQ)collector.nextResult(5000); + if (response == null || response.getType() == IQ.Type.ERROR) { + throw new XMPPException("Authentication failed."); + } + // We're done with the collector, so explicitly cancel it. + collector.cancel(); + // Set presence to online. + packetWriter.sendPacket(new Presence(true)); + } + + /** + * Creates a new chat with the specified participant. The participant should + * be a valid Jabber user such as jdoe@jivesoftware.com or + * jdoe@jivesoftware.com/work. + * + * @param participant the person to start the conversation with. + * @return a new Chat object. + */ + public Chat createChat(String participant) { + if (!isConnected()) { + throw new IllegalStateException("Not connected to server."); + } + return new Chat(this, participant); + } + + /** + * Creates a new group chat connected to the specified room. The room name + * should be a valid conference id, such as chatroom@jivesoftware.com. + * + * @param room the name of the room. + * @return a new GroupChat object. + */ + public GroupChat createGroupChat(String room) { + if (!isConnected()) { + throw new IllegalStateException("Not connected to server."); + } + return new GroupChat(this, room); + } + + /** + * Returns true if currently connected to the Jabber server. + * + * @return true if connected. + */ + public boolean isConnected() { + return connected; + } + + /** + * Set presence to unavailable then and closes the connection to the Jabber server. + * Once a connection has been closed, it cannot be re-opened. + */ + public void close() { + // Set presence to offline. + packetWriter.sendPacket(new Presence(false)); + packetWriter.shutdown(); + packetReader.shutdown(); + try { + socket.close(); + } + catch (Exception e) { } + connected = false; + } + + /** + * Returns the packet writer for this connection. This exposes the ability to directly + * write Packet objects to the Jabber server. In general, this is only required for + * advanced uses of the API. + * + * @return the packet writer for the connection. + */ + public PacketWriter getPacketWriter() { + if (!isConnected()) { + throw new IllegalStateException("Not connected to server."); + } + return packetWriter; + } + + /** + * Returns the packet reader for this connection. This exposes the ability to register + * listeners for incoming Packet objects. In general, this is only required for advanced + * uses of the API. + * + * @return the packet reader for the connection. + */ + public PacketReader getPacketReader() { + if (!isConnected()) { + throw new IllegalStateException("Not connected to server."); + } + return packetReader; + } + + /** + * Initializes the connection by creating a packet reader and writer and opening a + * Jabber stream to the server. + * + * @throws XMPPException if establishing a connection to the server fails. + */ + protected void init() throws XMPPException { + try { + reader = new InputStreamReader(socket.getInputStream(), "UTF-8"); + writer = new OutputStreamWriter(socket.getOutputStream(), "UTF-8"); + } + catch (IOException ioe) { + throw new XMPPException("Error establishing connection with server.", ioe); + } + + // If debugging is enabled, we open a window and write out all network traffic. + // The method that creates the debug GUI returns a thread that we must start after + // the packet reader and writer are created. + Thread allPacketListener = null; + if (DEBUG_ENABLED) { + allPacketListener = createDebug(); + } + + packetWriter = new PacketWriter(this); + packetReader = new PacketReader(this); + + // If debugging is enabled, we should start the thread that will listen for + // all packets and then log them. + if (DEBUG_ENABLED) { + allPacketListener.start(); + } + // Start the packet writer. This will open a Jabber stream to the server + packetWriter.start(); + // Start the packet reader. The startup() method will block until we + // get an opening stream packet back from server. + packetReader.startup(); + + // Make note of the fact that we're now connected. + connected = true; + } + + /** + * Creates the debug process, which is a GUI window that displays XML traffic. + * This method must be called before the packet reader and writer are called because + * it wraps the reader and writer objects with special logging implementations. + * The method returns a Thread that must be started after the packet reader and writer + * are started. + * + * @return a Thread used by the debugging process that must be started after the packet + * reader and writer are created. + */ + private Thread createDebug() { + // Use the native look and feel. + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch (Exception e) { + e.printStackTrace(); + } + + JFrame frame = new JFrame("Smack Debug Window -- " + getHost() + ":" + getPort()); + + // We'll arrange the UI into four tabs. The first tab contains all data, the second + // client generated XML, the third server generated XML, and the fourth is packet + // data from the server as seen by Smack. + JTabbedPane tabbedPane = new JTabbedPane(); + + JPanel allPane = new JPanel(); + allPane.setLayout(new GridLayout(3, 1)); + tabbedPane.add("All", allPane); + + // Create UI elements for client generated XML traffic. + final JTextArea sentText1 = new JTextArea(); + final JTextArea sentText2 = new JTextArea(); + sentText1.setEditable(false); + sentText2.setEditable(false); + sentText1.setForeground(new Color(112, 3, 3)); + sentText2.setForeground(new Color(112, 3, 3)); + allPane.add(new JScrollPane(sentText1)); + tabbedPane.add("Client", new JScrollPane(sentText2)); + + // Create UI elements for server generated XML traffic. + final JTextArea receivedText1 = new JTextArea(); + final JTextArea receivedText2 = new JTextArea(); + receivedText1.setEditable(false); + receivedText2.setEditable(false); + receivedText1.setForeground(new Color(6, 76, 133)); + receivedText2.setForeground(new Color(6, 76, 133)); + allPane.add(new JScrollPane(receivedText1)); + tabbedPane.add("Server", new JScrollPane(receivedText2)); + + // Create UI elements for interpreted XML traffic. + final JTextArea interpretedText1 = new JTextArea(); + final JTextArea interpretedText2 = new JTextArea(); + interpretedText1.setEditable(false); + interpretedText2.setEditable(false); + interpretedText1.setForeground(new Color(1, 94, 35)); + interpretedText2.setForeground(new Color(1, 94, 35)); + allPane.add(new JScrollPane(interpretedText1)); + tabbedPane.add("Interpreted Packets", new JScrollPane(interpretedText2)); + + frame.getContentPane().add(tabbedPane); + + frame.setSize(550, 400); + frame.show(); + + // Create a special Reader that wraps the main Reader and logs data to the GUI. + Reader debugReader = new Reader() { + + Reader myReader = reader; + + public int read(char cbuf[], int off, int len) throws IOException { + int count = myReader.read(cbuf, off, len); + String str = new String(cbuf, off, count); + receivedText1.append(str); + receivedText2.append(str); + if (str.endsWith(">")) { + receivedText1.append(NEWLINE); + receivedText2.append(NEWLINE); + } + return count; + } + + public void close() throws IOException { + myReader.close(); + } + + public int read() throws IOException { + return myReader.read(); + } + + public int read(char cbuf[]) throws IOException { + return myReader.read(cbuf); + } + + public long skip(long n) throws IOException { + return myReader.skip(n); + } + + public boolean ready() throws IOException { + return myReader.ready(); + } + + public boolean markSupported() { + return myReader.markSupported(); + } + + public void mark(int readAheadLimit) throws IOException { + myReader.mark(readAheadLimit); + } + + public void reset() throws IOException { + myReader.reset(); + } + }; + + // Create a special Writer that wraps the main Writer and logs data to the GUI. + Writer debugWriter = new Writer() { + + Writer myWriter = writer; + + public void write(char cbuf[], int off, int len) throws IOException { + myWriter.write(cbuf, off, len); + String str = new String(cbuf, off, len); + sentText1.append(str); + sentText2.append(str); + if (str.endsWith(">")) { + sentText1.append(NEWLINE); + sentText2.append(NEWLINE); + } + } + + public void flush() throws IOException { + myWriter.flush(); + } + + public void close() throws IOException { + myWriter.close(); + } + + public void write(int c) throws IOException { + myWriter.write(c); + } + + public void write(char cbuf[]) throws IOException { + myWriter.write(cbuf); + String str = new String(cbuf); + sentText1.append(str); + sentText2.append(str); + if (str.endsWith(">")) { + sentText1.append(NEWLINE); + sentText2.append(NEWLINE); + } + } + + public void write(String str) throws IOException { + myWriter.write(str); + sentText1.append(str); + sentText2.append(str); + if (str.endsWith(">")) { + sentText1.append(NEWLINE); + sentText2.append(NEWLINE); + } + } + + public void write(String str, int off, int len) throws IOException { + myWriter.write(str, off, len); + str = str.substring(off, off + len); + sentText1.append(str); + sentText2.append(str); + if (str.endsWith(">")) { + sentText1.append(NEWLINE); + sentText2.append(NEWLINE); + } + } + }; + + // Assign the reader/writer objects to use the debug versions. The packet reader + // and writer will use the debug versions when they are created. + reader = debugReader; + writer = debugWriter; + + // Create a thread that will listen for all incoming packets and write them to + // the GUI. This is what we call "interpreted" packet data, since it's the packet + // data as Smack sees it and not as it's coming in as raw XML. + Thread allPacketListener = new Thread() { + public void run() { + PacketCollector collector = packetReader.createPacketCollector(null); + while (true) { + Packet packet = collector.nextResult(); + interpretedText1.append(packet.toXML()); + interpretedText2.append(packet.toXML()); + interpretedText1.append(NEWLINE); + interpretedText2.append(NEWLINE); + } + } + }; + return allPacketListener; + } +} diff --git a/source/org/jivesoftware/smack/XMPPException.java b/source/org/jivesoftware/smack/XMPPException.java new file mode 100644 index 000000000..0801e89f1 --- /dev/null +++ b/source/org/jivesoftware/smack/XMPPException.java @@ -0,0 +1,73 @@ +/** + * $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@coolservlets.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; + + +public class XMPPException extends Exception { + + public XMPPException() { + super(); + } + + public XMPPException(String message) { + super(message); + } + + public XMPPException(Throwable cause) { + super(cause); + } + + public XMPPException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/source/org/jivesoftware/smack/filter/AndFilter.java b/source/org/jivesoftware/smack/filter/AndFilter.java new file mode 100644 index 000000000..f6bdc0e9c --- /dev/null +++ b/source/org/jivesoftware/smack/filter/AndFilter.java @@ -0,0 +1,85 @@ +/** + * $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@coolservlets.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.filter; + +import org.jivesoftware.smack.packet.Packet; + +/** + * Implements the logical AND operation over two packet filters. In other words, packets + * pass this filter if they pass both of the filters. + * + * @author Matt Tucker + */ +public class AndFilter implements PacketFilter { + + private PacketFilter filter1; + private PacketFilter filter2; + + /** + * Creates an AND filter using the specified filters. + * + * @param filter1 the first packet filter. + * @param filter2 the second packet filter. + */ + public AndFilter(PacketFilter filter1, PacketFilter filter2) { + if (filter1 == null || filter2 == null) { + throw new IllegalArgumentException("Parameters cannot be null."); + } + this.filter1 = filter1; + this.filter2 = filter2; + } + + public boolean accept(Packet packet) { + return filter1.accept(packet) && filter2.accept(packet); + } +} diff --git a/source/org/jivesoftware/smack/filter/FromContainsFilter.java b/source/org/jivesoftware/smack/filter/FromContainsFilter.java new file mode 100644 index 000000000..b0fcffa88 --- /dev/null +++ b/source/org/jivesoftware/smack/filter/FromContainsFilter.java @@ -0,0 +1,81 @@ +/** + * $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@coolservlets.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.filter; + +import org.jivesoftware.smack.packet.Packet; + +/** + * Filters for packets where the "from" field contains a specified value. + * + * @author Matt Tucker + */ +public class FromContainsFilter implements PacketFilter { + + private String from; + + /** + * Creates a "to" contains filter using the "to" field part. + * + * @param from the from field value the packet must contain. + */ + public FromContainsFilter(String from) { + if (from == null) { + throw new IllegalArgumentException("Parameter cannot be null."); + } + this.from = from; + } + + public boolean accept(Packet packet) { + return packet.getFrom().indexOf(from) != -1; + } +} diff --git a/source/org/jivesoftware/smack/filter/OrFilter.java b/source/org/jivesoftware/smack/filter/OrFilter.java new file mode 100644 index 000000000..a0590a2cd --- /dev/null +++ b/source/org/jivesoftware/smack/filter/OrFilter.java @@ -0,0 +1,85 @@ +/** + * $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@coolservlets.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.filter; + +import org.jivesoftware.smack.packet.Packet; + +/** + * Implements the logical OR operation over two packet filters. In other words, packets + * pass this filter if they pass either of the filters. + * + * @author Matt Tucker + */ +public class OrFilter implements PacketFilter { + + private PacketFilter filter1; + private PacketFilter filter2; + + /** + * Creates an OR filter using the specified filters. + * + * @param filter1 the first packet filter. + * @param filter2 the second packet filter. + */ + public OrFilter(PacketFilter filter1, PacketFilter filter2) { + if (filter1 == null || filter2 == null) { + throw new IllegalArgumentException("Parameters cannot be null."); + } + this.filter1 = filter1; + this.filter2 = filter2; + } + + public boolean accept(Packet packet) { + return filter1.accept(packet) || filter2.accept(packet); + } +} diff --git a/source/org/jivesoftware/smack/filter/PacketFilter.java b/source/org/jivesoftware/smack/filter/PacketFilter.java new file mode 100644 index 000000000..e1e19c987 --- /dev/null +++ b/source/org/jivesoftware/smack/filter/PacketFilter.java @@ -0,0 +1,95 @@ +/** + * $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@coolservlets.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.filter; + +import org.jivesoftware.smack.packet.Packet; + +/** + * Defines a way to filter packets for particular attributes. Packet filters are + * used when constructing packet listeners or collectors -- the filter defines + * what packets match the criteria of the collector or listener for further + * packet processing.
+ * + * Several pre-defined filters are defined. These filters can be logically combined + * for more complex packet filtering by using the + * {@link org.jivesoftware.smack.filter.AndFilter AndFilter} and + * {@link org.jivesoftware.smack.filter.OrFilter OrFilter} filters. It's also possible + * to define your own filters by implementing this interface. The code example below + * creates a trivial filter for packets with a specific ID. + * + *
+ * // Use an anonymous inner class to define a packet filter that returns + * // all packets that have a packet ID of "RS145". + * PacketFilter myFilter = new PacketFilter() { + * public boolean accept(Packet packet) { + * return "RS145".equals(packet.getPacketID()); + * } + * }; + * // Create a new packet collector using the filter we created. + * PacketCollector myCollector = packetReader.createPacketCollector(myFilter); + *+ * + * @see org.jivesoftware.smack.PacketCollector + * @see org.jivesoftware.smack.PacketListener + * @author Matt Tucker + */ +public interface PacketFilter { + + /** + * Tests whether or not the specified packet should pass the filter. + * + * @param packet the packet to test. + * @return true if and only if packet passes the filter. + */ + public boolean accept(Packet packet); +} diff --git a/source/org/jivesoftware/smack/filter/PacketIDFilter.java b/source/org/jivesoftware/smack/filter/PacketIDFilter.java new file mode 100644 index 000000000..1f3609318 --- /dev/null +++ b/source/org/jivesoftware/smack/filter/PacketIDFilter.java @@ -0,0 +1,81 @@ +/** + * $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@coolservlets.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.filter; + +import org.jivesoftware.smack.packet.Packet; + +/** + * Filters for packets with a particular packet ID. + * + * @author Matt Tucker + */ +public class PacketIDFilter implements PacketFilter { + + private String packetID; + + /** + * Creates a new packet ID filter using the specified packet ID. + * + * @param packetID the packet ID to filter for. + */ + public PacketIDFilter(String packetID) { + if (packetID == null) { + throw new IllegalArgumentException("Packet ID cannot be null."); + } + this.packetID = packetID; + } + + public boolean accept(Packet packet) { + return packetID.equals(packet.getPacketID()); + } +} diff --git a/source/org/jivesoftware/smack/filter/PacketTypeFilter.java b/source/org/jivesoftware/smack/filter/PacketTypeFilter.java new file mode 100644 index 000000000..56a1c9635 --- /dev/null +++ b/source/org/jivesoftware/smack/filter/PacketTypeFilter.java @@ -0,0 +1,90 @@ +/** + * $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@coolservlets.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.filter; + +import org.jivesoftware.smack.packet.Packet; + +/** + * Filters for packets of a particular type. The type is given as a Class object, so + * example types would: + *
Code | Description |
302 | Redirect |
400 | Bad Request |
401 | Unauthorized |
402 | Payment Required |
403 | Forbidden |
404 | Not Found |
405 | Not Allowed |
406 | Not Acceptable |
407 | Registration Required |
408 | Request Timeout |
409 | Conflict |
500 | Internal Server Error |
501 | Not Implemented |
502 | Remote Server Error |
503 | Service Unavailable |
504 | Remote Server Timeout |
").append(code).append("
");
+ if (message != null) {
+ buf.append("Message type | |||||
Field | Normal | Chat | Group Chat | Headline | Error |
subject | SHOULD | SHOULD NOT | SHOULD NOT | SHOULD NOT | SHOULD NOT |
thread | OPTIONAL | SHOULD | OPTIONAL | OPTIONAL | SHOULD NOT |
body | SHOULD | SHOULD | SHOULD | SHOULD | SHOULD NOT |
error | MUST NOT | MUST NOT | MUST NOT | MUST NOT | MUST |
+ * A hash is a one-way function -- that is, given an + * input, an output is easily computed. However, given the output, the + * input is almost impossible to compute. This is useful for passwords + * since we can store the hash and a hacker will then have a very hard time + * determining the original password. + * + * @param data the String to compute the hash of. + * @return a hashed version of the passed-in String + */ + public synchronized static final String hash(String data) { + if (digest == null) { + try { + digest = MessageDigest.getInstance("SHA-1"); + } + catch (NoSuchAlgorithmException nsae) { + System.err.println("Failed to load the SHA-1 MessageDigest. " + + "Jive will be unable to function normally."); + } + } + // Now, compute hash. + try { + digest.update(data.getBytes("UTF-8")); + } + catch (UnsupportedEncodingException e) { + System.err.println(e); + } + return encodeHex(digest.digest()); + } + + /** + * Turns an array of bytes into a String representing each byte as an + * unsigned hex number. + *
+ * Method by Santeri Paavolainen, Helsinki Finland 1996
+ * (c) Santeri Paavolainen, Helsinki Finland 1996
+ * Distributed under LGPL.
+ *
+ * @param bytes an array of bytes to convert to a hex-string
+ * @return generated hex string
+ */
+ public static final String encodeHex(byte[] bytes) {
+ StringBuffer buf = new StringBuffer(bytes.length * 2);
+ int i;
+
+ for (i = 0; i < bytes.length; i++) {
+ if (((int) bytes[i] & 0xff) < 0x10) {
+ buf.append("0");
+ }
+ buf.append(Long.toString((int) bytes[i] & 0xff, 16));
+ }
+ return buf.toString();
+ }
+
+ //*********************************************************************
+ //* Base64 - a simple base64 encoder and decoder.
+ //*
+ //* Copyright (c) 1999, Bob Withers - bwit@pobox.com
+ //*
+ //* This code may be freely used for any purpose, either personal
+ //* or commercial, provided the authors copyright notice remains
+ //* intact.
+ //*********************************************************************
+
+ private static final int fillchar = '=';
+ private static final String cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ + "0123456789+/";
+
+ /**
+ * Encodes a String as a base64 String.
+ *
+ * @param data a String to encode.
+ * @return a base64 encoded String.
+ */
+ public static String encodeBase64(String data) {
+ byte [] bytes = null;
+ try {
+ bytes = data.getBytes("ISO-8859-1");
+ }
+ catch (UnsupportedEncodingException uee) {
+ uee.printStackTrace();
+ }
+ return encodeBase64(bytes);
+ }
+
+ /**
+ * Encodes a byte array into a base64 String.
+ *
+ * @param data a byte array to encode.
+ * @return a base64 encode String.
+ */
+ public static String encodeBase64(byte[] data) {
+ int c;
+ int len = data.length;
+ StringBuffer ret = new StringBuffer(((len / 3) + 1) * 4);
+ for (int i = 0; i < len; ++i) {
+ c = (data[i] >> 2) & 0x3f;
+ ret.append(cvt.charAt(c));
+ c = (data[i] << 4) & 0x3f;
+ if (++i < len)
+ c |= (data[i] >> 4) & 0x0f;
+
+ ret.append(cvt.charAt(c));
+ if (i < len) {
+ c = (data[i] << 2) & 0x3f;
+ if (++i < len)
+ c |= (data[i] >> 6) & 0x03;
+
+ ret.append(cvt.charAt(c));
+ }
+ else {
+ ++i;
+ ret.append((char) fillchar);
+ }
+
+ if (i < len) {
+ c = data[i] & 0x3f;
+ ret.append(cvt.charAt(c));
+ }
+ else {
+ ret.append((char) fillchar);
+ }
+ }
+ return ret.toString();
+ }
+
+ /**
+ * Decodes a base64 String.
+ *
+ * @param data a base64 encoded String to decode.
+ * @return the decoded String.
+ */
+ public static String decodeBase64(String data) {
+ byte [] bytes = null;
+ try {
+ bytes = data.getBytes("ISO-8859-1");
+ }
+ catch (UnsupportedEncodingException uee) {
+ uee.printStackTrace();
+ }
+ return decodeBase64(bytes);
+ }
+
+ /**
+ * Decodes a base64 aray of bytes.
+ *
+ * @param data a base64 encode byte array to decode.
+ * @return the decoded String.
+ */
+ public static String decodeBase64(byte[] data) {
+ int c, c1;
+ int len = data.length;
+ StringBuffer ret = new StringBuffer((len * 3) / 4);
+ for (int i = 0; i < len; ++i) {
+ c = cvt.indexOf(data[i]);
+ ++i;
+ c1 = cvt.indexOf(data[i]);
+ c = ((c << 2) | ((c1 >> 4) & 0x3));
+ ret.append((char) c);
+ if (++i < len) {
+ c = data[i];
+ if (fillchar == c)
+ break;
+
+ c = cvt.indexOf(c);
+ c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
+ ret.append((char) c1);
+ }
+
+ if (++i < len) {
+ c1 = data[i];
+ if (fillchar == c1)
+ break;
+
+ c1 = cvt.indexOf(c1);
+ c = ((c << 6) & 0xc0) | c1;
+ ret.append((char) c);
+ }
+ }
+ return ret.toString();
+ }
+
+ /**
+ * Pseudo-random number generator object for use with randomString().
+ * The Random class is not considered to be cryptographically secure, so
+ * only use these random Strings for low to medium security applications.
+ */
+ private static Random randGen = new Random();
+
+ /**
+ * Array of numbers and letters of mixed case. Numbers appear in the list
+ * twice so that there is a more equal chance that a number will be picked.
+ * We can use the array to get a random number or letter by picking a random
+ * array index.
+ */
+ private static char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" +
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
+
+ /**
+ * Returns a random String of numbers and letters (lower and upper case)
+ * of the specified length. The method uses the Random class that is
+ * built-in to Java which is suitable for low to medium grade security uses.
+ * This means that the output is only pseudo random, i.e., each number is
+ * mathematically generated so is not truly random.
+ *
+ * The specified length must be at least one. If not, the method will return
+ * null.
+ *
+ * @param length the desired length of the random String to return.
+ * @return a random String of numbers and letters of the specified length.
+ */
+ public static final String randomString(int length) {
+ if (length < 1) {
+ return null;
+ }
+ // Create a char buffer to put random letters and numbers in.
+ char [] randBuffer = new char[length];
+ for (int i=0; i