From f3837581f88996cde4d33f8c5b5a83229b8c7a9f Mon Sep 17 00:00:00 2001 From: Matt Tucker Date: Mon, 13 Jan 2003 16:58:47 +0000 Subject: [PATCH] Importing. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@1777 b35dd754-fafc-0310-a699-88a17e54d16e --- build/lib/xpp.jar | Bin 0 -> 46950 bytes source/org/jivesoftware/smack/Chat.java | 230 ++++++++ source/org/jivesoftware/smack/GroupChat.java | 189 +++++++ .../jivesoftware/smack/PacketCollector.java | 186 ++++++ .../jivesoftware/smack/PacketListener.java | 77 +++ .../org/jivesoftware/smack/PacketReader.java | 460 +++++++++++++++ .../org/jivesoftware/smack/PacketWriter.java | 145 +++++ .../jivesoftware/smack/SSLXMPPConnection.java | 176 ++++++ .../jivesoftware/smack/XMPPConnection.java | 532 ++++++++++++++++++ .../org/jivesoftware/smack/XMPPException.java | 73 +++ .../jivesoftware/smack/filter/AndFilter.java | 85 +++ .../smack/filter/FromContainsFilter.java | 81 +++ .../jivesoftware/smack/filter/OrFilter.java | 85 +++ .../smack/filter/PacketFilter.java | 95 ++++ .../smack/filter/PacketIDFilter.java | 81 +++ .../smack/filter/PacketTypeFilter.java | 90 +++ .../smack/filter/ThreadFilter.java | 87 +++ .../smack/filter/ToContainsFilter.java | 82 +++ .../smack/packet/Authentication.java | 168 ++++++ .../org/jivesoftware/smack/packet/Error.java | 141 +++++ source/org/jivesoftware/smack/packet/IQ.java | 164 ++++++ .../jivesoftware/smack/packet/Message.java | 320 +++++++++++ .../org/jivesoftware/smack/packet/Packet.java | 348 ++++++++++++ .../jivesoftware/smack/packet/Presence.java | 263 +++++++++ .../jivesoftware/smack/util/StringUtils.java | 364 ++++++++++++ 25 files changed, 4522 insertions(+) create mode 100644 build/lib/xpp.jar create mode 100644 source/org/jivesoftware/smack/Chat.java create mode 100644 source/org/jivesoftware/smack/GroupChat.java create mode 100644 source/org/jivesoftware/smack/PacketCollector.java create mode 100644 source/org/jivesoftware/smack/PacketListener.java create mode 100644 source/org/jivesoftware/smack/PacketReader.java create mode 100644 source/org/jivesoftware/smack/PacketWriter.java create mode 100644 source/org/jivesoftware/smack/SSLXMPPConnection.java create mode 100644 source/org/jivesoftware/smack/XMPPConnection.java create mode 100644 source/org/jivesoftware/smack/XMPPException.java create mode 100644 source/org/jivesoftware/smack/filter/AndFilter.java create mode 100644 source/org/jivesoftware/smack/filter/FromContainsFilter.java create mode 100644 source/org/jivesoftware/smack/filter/OrFilter.java create mode 100644 source/org/jivesoftware/smack/filter/PacketFilter.java create mode 100644 source/org/jivesoftware/smack/filter/PacketIDFilter.java create mode 100644 source/org/jivesoftware/smack/filter/PacketTypeFilter.java create mode 100644 source/org/jivesoftware/smack/filter/ThreadFilter.java create mode 100644 source/org/jivesoftware/smack/filter/ToContainsFilter.java create mode 100644 source/org/jivesoftware/smack/packet/Authentication.java create mode 100644 source/org/jivesoftware/smack/packet/Error.java create mode 100644 source/org/jivesoftware/smack/packet/IQ.java create mode 100644 source/org/jivesoftware/smack/packet/Message.java create mode 100644 source/org/jivesoftware/smack/packet/Packet.java create mode 100644 source/org/jivesoftware/smack/packet/Presence.java create mode 100644 source/org/jivesoftware/smack/util/StringUtils.java diff --git a/build/lib/xpp.jar b/build/lib/xpp.jar new file mode 100644 index 0000000000000000000000000000000000000000..7418dbf9498df7900703f2ede51c3b7d6db690dc GIT binary patch literal 46950 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 literal 0 HcmV?d00001 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"); + writer.write(stream.toString()); + writer.flush(); + // Write out packets from the queue. + while (!done) { + Packet packet = nextPacket(); + writer.write(packet.toXML()); + writer.flush(); + } + // Close the stream. + try { + writer.write(""); + writer.flush(); + } + catch (Exception e) { } + finally { + try { + writer.close(); + } + catch (Exception e) { } + } + } + catch (IOException ioe){ + ioe.printStackTrace(); + } + } + + public void shutdown() { + done = true; + } +} + + diff --git a/source/org/jivesoftware/smack/SSLXMPPConnection.java b/source/org/jivesoftware/smack/SSLXMPPConnection.java new file mode 100644 index 000000000..5d75d6118 --- /dev/null +++ b/source/org/jivesoftware/smack/SSLXMPPConnection.java @@ -0,0 +1,176 @@ +/** + * $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 java.io.*; +import java.net.*; +import java.security.NoSuchAlgorithmException; +import java.security.KeyManagementException; +import java.security.cert.*; +import javax.net.ssl.*; +import javax.net.*; + +/** + * Creates an SSL connection to a XMPP (Jabber) server. + * + * @author Matt Tucker + */ +public class SSLXMPPConnection extends XMPPConnection { + + public SSLXMPPConnection(String host) throws XMPPException { + this(host, 5223); + } + + public SSLXMPPConnection(String host, int port) throws XMPPException { + this.host = host; + this.port = port; + try { + SSLSocketFactory sslFactory = new DummySSLSocketFactory(); + this.socket = (SSLSocket)sslFactory.createSocket(host, port); + } + catch (UnknownHostException uhe) { + throw new XMPPException("Could not connect to " + host + ":" + port + ".", uhe); + } + catch (IOException ioe) { + throw new XMPPException("Error connecting to " + host + ":" + port + ".", ioe); + } + super.init(); + } + + /** + * An SSL socket factory that will let any certifacte past, even if it's expired or + * not singed by a root CA. + */ + private static class DummySSLSocketFactory extends SSLSocketFactory { + + private SSLSocketFactory factory; + + public DummySSLSocketFactory() { + + try { + SSLContext sslcontent = SSLContext.getInstance("TLS"); + sslcontent.init(null, // KeyManager not required + new TrustManager[] { new DummyTrustManager() }, + new java.security.SecureRandom()); + factory = sslcontent.getSocketFactory(); + } + catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + catch (KeyManagementException e) { + e.printStackTrace(); + } + } + + public static SocketFactory getDefault() { + return new DummySSLSocketFactory(); + } + + public Socket createSocket(Socket socket, String s, int i, boolean flag) + throws IOException + { + return factory.createSocket(socket, s, i, flag); + } + + public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr2, int j) + throws IOException + { + return factory.createSocket(inaddr, i, inaddr2, j); + } + + public Socket createSocket(InetAddress inaddr, int i) throws IOException { + return factory.createSocket(inaddr, i); + } + + public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException { + return factory.createSocket(s, i, inaddr, j); + } + + public Socket createSocket(String s, int i) throws IOException { + return factory.createSocket(s, i); + } + + public String[] getDefaultCipherSuites() { + return factory.getSupportedCipherSuites(); + } + + public String[] getSupportedCipherSuites() { + return factory.getSupportedCipherSuites(); + } + } + + /** + * Trust manager which accepts certificates without any validation + * except date validation. + */ + private static class DummyTrustManager implements X509TrustManager { + + public void checkClientTrusted(X509Certificate[] chain, String authType) { + + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) { + try { + chain[0].checkValidity(); + } + catch (CertificateExpiredException e) { + } + catch (CertificateNotYetValidException e) { + } + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } +} diff --git a/source/org/jivesoftware/smack/XMPPConnection.java b/source/org/jivesoftware/smack/XMPPConnection.java new file mode 100644 index 000000000..b28c03ba4 --- /dev/null +++ b/source/org/jivesoftware/smack/XMPPConnection.java @@ -0,0 +1,532 @@ +/** + * $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.*; +import org.jivesoftware.smack.filter.PacketIDFilter; + +import javax.swing.*; +import java.net.*; +import java.io.*; +import java.awt.*; + +/** + * Creates a connection to a XMPP (Jabber) server. A simple use of this API might + * look like the following: + *
+ * // 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:
    + *
  • Client Traffic -- raw XML traffic generated by Smack and sent to the server. + *
  • Server Traffic -- raw XML traffic sent by the server to the client. + *
  • Interpreted Packets -- shows XML packets from the server as parsed by Smack. + *
+ * + * Debugging can be enabled by setting this field to true, or by setting the Java system + * property smack.debugEnabled to true. The system property can be set on the + * command line such as "java SomeApp -Dsmack.debugEnabled=true". + */ + public static boolean DEBUG_ENABLED = Boolean.getBoolean("smack.debugEnabled"); + + protected String host; + protected int port; + protected Socket socket; + + String connectionID; + private boolean connected = false; + + private PacketWriter packetWriter; + private PacketReader packetReader; + + Writer writer; + Reader reader; + + /** + * Constructor for use by classes extending this one. + */ + protected XMPPConnection() { + + } + + /** + * Creates a new connection to the specified Jabber server. The default port of 5222 will + * be used. + * + * @param host the name of the jabber server to connect to; e.g. jivesoftware.com. + * @throws XMPPException if an error occurs while trying to establish a connection. + */ + public XMPPConnection(String host) throws XMPPException { + this(host, 5222); + } + + /** + * Creates a new connection to the to the specified Jabber server on the given port. + * + * @param host the name of the jabber server to connect to; e.g. jivesoftware.com. + * @param port the port on the server that should be used; e.g. 5222. + * @throws XMPPException if an error occurs while trying to establish a connection. + */ + public XMPPConnection(String host, int port) throws XMPPException { + this.host = host; + this.port = port; + try { + this.socket = new Socket(host, port); + } + catch (UnknownHostException uhe) { + throw new XMPPException("Could not connect to " + host + ":" + port + ".", uhe); + } + catch (IOException ioe) { + throw new XMPPException("Error connecting to " + host + ":" + port + ".", ioe); + } + init(); + } + + /** + * Returns the connection ID for this connection, which is the value set by the server + * when opening a Jabber stream. If the server does not set a connection ID, this value + * will be null. + * + * @return the ID of this connection returned from the Jabber server. + */ + public String getConnectionID() { + return connectionID; + } + + /** + * Returns the host name of the Jabber server for this connection. + * + * @return the host name of the Jabber server. + */ + public String getHost() { + return host; + } + + /** + * Returns the port number of the XMPP server for this connection. The default port + * for normal connections is 5222. The default port for SSL connections is 5223. + * + * @return the port number of the Jabber server. + */ + public int getPort() { + return port; + } + + /** + * Login to the server using the server's preferred authentication mechanism and set + * our presence to available. If more than five seconds elapses without a response from + * the server, or if an error occurs, a XMPPException will be thrown.

+ * + * @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: + *
    + *
  • Message.class + *
  • IQ.class + *
  • Presence.class + *
+ * + * @author Matt Tucker + */ +public class PacketTypeFilter implements PacketFilter { + + Class packetType; + + /** + * Creates a new packet type filter that will filter for packets that are the + * same type as packetType. + * + * @param packetType the Class type. + */ + public PacketTypeFilter(Class packetType) { + // Ensure the packet type is a sub-class of Packet. + if (!Packet.class.isAssignableFrom(packetType)) { + throw new IllegalArgumentException("Packet type must be a sub-class of Packet."); + } + this.packetType = packetType; + } + + public boolean accept(Packet packet) { + return packetType.isInstance(packet); + } + +} diff --git a/source/org/jivesoftware/smack/filter/ThreadFilter.java b/source/org/jivesoftware/smack/filter/ThreadFilter.java new file mode 100644 index 000000000..a7868bb4f --- /dev/null +++ b/source/org/jivesoftware/smack/filter/ThreadFilter.java @@ -0,0 +1,87 @@ +/** + * $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; +import org.jivesoftware.smack.packet.Message; + +/** + * Filters for message packets with a particular thread value. + * + * @author Matt Tucker + */ +public class ThreadFilter implements PacketFilter { + + private String thread; + + /** + * Creates a new thread filter using the specified thread value. + * + * @param thread the thread value to filter for. + */ + public ThreadFilter(String thread) { + if (thread == null) { + throw new IllegalArgumentException("Thread cannot be null."); + } + this.thread = thread; + } + + public boolean accept(Packet packet) { + if (packet instanceof Message) { + return thread.equals(((Message)packet).getThread()); + } + else { + return false; + } + } +} diff --git a/source/org/jivesoftware/smack/filter/ToContainsFilter.java b/source/org/jivesoftware/smack/filter/ToContainsFilter.java new file mode 100644 index 000000000..742e51cd8 --- /dev/null +++ b/source/org/jivesoftware/smack/filter/ToContainsFilter.java @@ -0,0 +1,82 @@ +/** + * $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 "to" field contains a specified value. For example, + * the filter could be used to listen for all packets sent to a group chat nickname. + * + * @author Matt Tucker + */ +public class ToContainsFilter implements PacketFilter { + + private String to; + + /** + * Creates a "to" contains filter using the "to" field part. + * + * @param to the to field value the packet must contain. + */ + public ToContainsFilter(String to) { + if (to == null) { + throw new IllegalArgumentException("Parameter cannot be null."); + } + this.to = to; + } + + public boolean accept(Packet packet) { + return packet.getTo().indexOf(to) != -1; + } +} diff --git a/source/org/jivesoftware/smack/packet/Authentication.java b/source/org/jivesoftware/smack/packet/Authentication.java new file mode 100644 index 000000000..793eb8e10 --- /dev/null +++ b/source/org/jivesoftware/smack/packet/Authentication.java @@ -0,0 +1,168 @@ +/** + * $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.packet; + +/** + * Authentication packet, which can be used to login to a Jabber server as well as discover + * login information from the server. + */ +public class Authentication extends IQ { + + private IQ.Type type; + private String username = null; + private String password = null; + private String resource = null; + + /** + * Creates a new Authentication object with the specified type. + * + * @param type the Type of authentication packet. + */ + public Authentication(IQ.Type type) { + this.type = type; + } + + public Authentication(String username, String password, String resource) { + this.username = username; + this.password = password; + this.resource = resource; + } + + /** + * Returns the type of the authentication packet. + * + * @return + */ + public Type getType() { + return type; + } + + /** + * Sets the type of the authentication packet. + * + * @param type + */ + public void setType(Type type) { + this.type = type; + } + + /** + * Returns the username. + * + * @return the username. + */ + public String getUsername() { + return username; + } + + /** + * Sets the username. + * + * @param username the username. + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * Returns the password. + * + * @return the password. + */ + public String getPassword() { + return password; + } + + /** + * Sets the password. + * + * @param password the password. + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Returns the resource. + * + * @return the resource. + */ + public String getResource() { + return resource; + } + + /** + * Sets the resource. + * + * @param resource the resource. + */ + public void setResource(String resource) { + this.resource = resource; + } + + public String getQueryXML() { + StringBuffer buf = new StringBuffer(); + buf.append(""); + if (username != null) { + buf.append("").append( username).append(""); + } + if (password != null) { + buf.append("").append(password).append(""); + } + if (resource != null) { + buf.append("").append(resource).append(""); + } + buf.append(""); + return buf.toString(); + } +} diff --git a/source/org/jivesoftware/smack/packet/Error.java b/source/org/jivesoftware/smack/packet/Error.java new file mode 100644 index 000000000..863c9ba8d --- /dev/null +++ b/source/org/jivesoftware/smack/packet/Error.java @@ -0,0 +1,141 @@ +/** + * $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.packet; + +/** + * Represents a XMPP error subpacket. Typically, a server responds to a request that has + * problems by sending the packet back and including an error packet. Each error has a code + * as well as as an optional text explanation. Typical error codes are as follows: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
CodeDescription
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
+ * + * @author Matt Tucker + */ +public class Error { + + private int code; + private String message; + + /** + * Creates a new error with the specified code and no message.. + * + * @param code the error code. + */ + public Error (int code) { + this.code = code; + this.message = null; + } + + /** + * Creates a new error with the specified code and message. + * + * @param code the error code. + * @param message a message describing the error. + */ + public Error(int code, String message) { + this.code = code; + this.message = message; + } + + /** + * Returns the error code. + * + * @return the error code. + */ + public int getCode() { + return code; + } + + /** + * Returns the message describing the error, or null if there is no message. + * + * @return the message describing the error, or null if there is no message. + */ + public String getMessage() { + return message; + } + + /** + * Returns the error as XML. + * + * @return the error as XML. + */ + public String toXML() { + StringBuffer buf = new StringBuffer(); + buf.append(""); + buf.append("").append(code).append(""); + if (message != null) { + buf.append("").append(message).append(""); + } + buf.append(""); + return buf.toString(); + } +} diff --git a/source/org/jivesoftware/smack/packet/IQ.java b/source/org/jivesoftware/smack/packet/IQ.java new file mode 100644 index 000000000..eecdefbbe --- /dev/null +++ b/source/org/jivesoftware/smack/packet/IQ.java @@ -0,0 +1,164 @@ +/** + * $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.packet; + +/** + * A generic IQ packet. + * + * @author Matt Tucker + */ +public abstract class IQ extends Packet { + + private Type type = Type.GET; + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public String toXML() { + StringBuffer buf = new StringBuffer(); + buf.append(""); + } + else { + buf.append("type=\"").append(getType()).append("\">"); + } + // Add the query section if there is one. + String queryXML = getQueryXML(); + if (queryXML != null) { + buf.append(queryXML); + } + buf.append(""); + // Add packet properties, if any are defined. + String propertiesXML = getPropertiesXML(); + if (propertiesXML != null) { + buf.append(propertiesXML); + } + // Add the error sub-packet, if there is one. + Error error = getError(); + if (error != null) { + buf.append(error.toXML()); + } + return buf.toString(); + } + + /** + * Implementations of this method should return the XML of the query section of + * an IQ packet if that section should exist, or null otherwise. This lets + * the majority of IQ XML writing be generic, with each sub-class providing just the + * packet specific XML. + * + * @return the query section of the IQ XML. + */ + public abstract String getQueryXML(); + + /** + * A class to represent the type of the IQ packet. Valid types are: + * + *
    + *
  • IQ.Type.GET + *
  • IQ.Type.SET + *
  • IQ.Type.RESULT + *
  • IQ.Type.ERROR + *
+ */ + public static class Type { + + public static final Type GET = new Type("get"); + public static final Type SET = new Type("set"); + public static final Type RESULT = new Type("result"); + public static final Type ERROR = new Type("error"); + + public static Type fromString(String type) { + if (type.equals(GET.toString())) { + return GET; + } + else if (type.equals(SET.toString())) { + return SET; + } + else if (type.equals(ERROR.toString())) { + return ERROR; + } + else if (type.equals(RESULT.toString())) { + return RESULT; + } + else { + return null; + } + } + + private String value; + + private Type(String value) { + this.value = value; + } + + public String toString() { + return value; + } + } +} diff --git a/source/org/jivesoftware/smack/packet/Message.java b/source/org/jivesoftware/smack/packet/Message.java new file mode 100644 index 000000000..285a7543d --- /dev/null +++ b/source/org/jivesoftware/smack/packet/Message.java @@ -0,0 +1,320 @@ +/** + * $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.packet; + +import org.jivesoftware.smack.util.StringUtils; + +/** + * Represents XMPP message packets. A message can be one of several types: + * + *
    + *
  • NORMAL -- (Default) a normal text message used in email like interface. + *
  • CHAT -- a typically short text message used in line-by-line chat interfaces. + *
  • GROUP_CHAT -- a chat message sent to a groupchat server for group chats. + *
  • HEADLINE -- a text message to be displayed in scrolling marquee displays. + *
  • ERROR -- indicates a messaging error. + *
+ * + * For each message type, different message fields are typically used as follows: + * + * + * + * + * + * + * + * + *
 Message type
FieldNormalChatGroup ChatHeadlineError
subject SHOULDSHOULD NOTSHOULD NOTSHOULD NOTSHOULD NOT
thread OPTIONALSHOULDOPTIONALOPTIONALSHOULD NOT
body SHOULDSHOULDSHOULDSHOULDSHOULD NOT
error MUST NOTMUST NOTMUST NOTMUST NOTMUST
+ * + * @author Matt Tucker + */ +public class Message extends Packet { + + public static final Type NORMAL = new Type("normal"); + public static final Type CHAT = new Type("chat"); + public static final Type GROUP_CHAT = new Type("groupchat"); + public static final Type HEADLINE = new Type("headline"); + public static final Type ERROR = new Type("error"); + + private String sender = null; + private String recipient = null; + private Type type = NORMAL; + private String subject = null; + private String body = null; + private String thread = null; + + /** + * Creates a new, "normal" message. + */ + public Message() { + + } + + /** + * Creates a new "normal" message to the specified recipient. + * + * @param recipient the recipient of the message. + */ + public Message(String recipient) { + this.recipient = recipient; + } + + /** + * Creates a new message of the specified type to a recipient. + * + * @param recipient the user to send the message to. + * @param type the message type. + */ + public Message(String recipient, Type type) { + if (recipient == null || type == null) { + throw new IllegalArgumentException("Parameters cannot be null."); + } + this.recipient = recipient; + this.type = type; + } + + /** + * Returns the sender of the message, or null if the sender has not been set. + * + * @return the sender of the message. + */ + public String getSender() { + return sender; + } + + /** + * Sets the sender of the message. + * + * @param sender the sender of the message. + */ + public void setSender(String sender) { + this.sender = sender; + } + + /** + * Returns the recipient of the message, or null if the recipient has not been set. + * + * @return the recipient of the message. + */ + public String getRecipient() { + return recipient; + } + + /** + * Sets the recipient of the message. + * + * @param recipient the recipient of the message. + */ + public void setRecipient(String recipient) { + if (recipient == null) { + throw new IllegalArgumentException("Recipient cannot be null."); + } + this.recipient = recipient; + } + + /** + * Returns the type of the message. + * + * @return the type of the message. + */ + public Type getType() { + return type; + } + + /** + * Sets the type of the message. + * + * @param type the type of the message. + */ + public void setType(Type type) { + if (type == null) { + throw new IllegalArgumentException("Type cannot be null."); + } + this.type = type; + } + + /** + * Returns the subject of the message, or null if the subject has not been set. + * The subject is a short description of message contents. + * + * @return the subject of the message. + */ + public String getSubject() { + return subject; + } + + /** + * Sets the subject of the message. The subject is a short description of + * message contents. + * + * @param subject the subject of the message. + */ + public void setSubject(String subject) { + this.subject = subject; + } + + /** + * Returns the body of the message, or null if the body has not been set. The body + * is the main message contents. + * + * @return the body of the message. + */ + public String getBody() { + return body; + } + + /** + * Sets the body of the message. The body is the main message contents. + * @param body + */ + public void setBody(String body) { + this.body = body; + } + + /** + * Returns the thread id of the message, which is a unique identifier for a sequence + * of "chat" messages. + * + * @return the thread id of the message. + */ + public String getThread() { + return thread; + } + + /** + * Sets the thread id of the message, which is a unique identifier for a sequence + * of "chat" messages. + * + * @param thread the thread id of the message. + */ + public void setThread(String thread) { + this.thread = thread; + } + + /** + * Returns the message as XML. + * + * @return the message as XML. + */ + public String toXML() { + StringBuffer buf = new StringBuffer(); + buf.append(""); + if (subject != null) { + buf.append("").append(StringUtils.escapeForXML(subject)).append(""); + } + if (body != null) { + buf.append("").append(StringUtils.escapeForXML(body)).append(""); + } + if (thread != null) { + buf.append("").append(thread).append(""); + } + // Append the error subpacket if the message type is an error. + if (type == ERROR) { + Error error = getError(); + if (error != null) { + buf.append(error.toXML()); + } + } + String properties = getPropertiesXML(); + if (properties != null) { + buf.append(properties); + } + buf.append(""); + return buf.toString(); + } + + /** + * Represents the type of a message. + */ + public static class Type { + + public static Type fromString(String type) { + if (type.equals(CHAT.toString())) { + return CHAT; + } + else if (type.equals(GROUP_CHAT.toString())) { + return GROUP_CHAT; + } + else if (type.equals(HEADLINE.toString())) { + return HEADLINE; + } + else if (type.equals(ERROR.toString())) { + return ERROR; + } + else { + return NORMAL; + } + } + + private String value; + + private Type(String value) { + this.value = value; + } + + public String toString() { + return value; + } + } +} diff --git a/source/org/jivesoftware/smack/packet/Packet.java b/source/org/jivesoftware/smack/packet/Packet.java new file mode 100644 index 000000000..a8cabf817 --- /dev/null +++ b/source/org/jivesoftware/smack/packet/Packet.java @@ -0,0 +1,348 @@ +/** + * $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.packet; + +import org.jivesoftware.smack.util.StringUtils; + +import java.util.*; +import java.io.*; + +/** + * Base class for XMPP packets. Every packet has a unique ID (which is automatically + * generated, but can be overriden). Optionally, the "to" and "from" fields can be set, + * as well as an arbitrary number of properties. + * + * Properties provide an easy mechanism for clients to share data. Each property has a + * String name, and a value that is a Java primitive (int, long, float, double, boolean) + * or any Serializable object (a Java object is Serializable when it implements the + * Serializable interface). + * + * @author Matt Tucker + */ +public abstract class Packet { + + /** + * 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 String packetID = nextID(); + private String to = null; + private String from = null; + private Map properties = new HashMap(); + private Error error = null; + + /** + * Returns the unique ID of the packet. + * + * @return the packet's unique ID. + */ + public String getPacketID() { + return packetID; + } + + /** + * Sets the unique ID of the packet. + * + * @param packetID the unique ID for the packet. + */ + public void setPacketID(String packetID) { + this.packetID = packetID; + } + + /** + * Returns who the packet is being sent "to", or null if + * the value is not set. The XMPP protocol often makes the "to" + * attribute optional, so it does not always need to be set. + * + * @return who the packet is being sent to, or null if the + * value has not been set. + */ + public String getTo() { + return to; + } + + /** + * Sets who the packet is being sent "to". The XMPP protocol often makes + * the "to" attribute optional, so it does not always need to be set. + * + * @param to who the packet is being sent to. + */ + public void setTo(String to) { + this.to = to; + } + + /** + * Returns who the packet is being sent "from" or null if + * the value is not set. The XMPP protocol often makes the "from" + * attribute optional, so it does not always need to be set. + * + * @return who the packet is being sent from, or null if the + * valud has not been set. + */ + public String getFrom() { + return from; + } + + /** + * Sets who the packet is being sent "from". The XMPP protocol often + * makes the "from" attribute optional, so it does not always need to + * be set. + * + * @param from who the packet is being sent to. + */ + public void setFrom(String from) { + this.from = from; + } + + /** + * Returns the error associated with this packet, or null if there are + * no errors. + * + * @return the error sub-packet or null if there isn't an error. + */ + public Error getError() { + return error; + } + + /** + * Sets the error for this packet. + * + * @param error the error to associate with this packet. + */ + public void setError(Error error) { + this.error = error; + } + + /** + * Returns the packet property with the specified name or null if the + * property doesn't exist. Property values that were orginally primitives will + * be returned as their object equivalent. For example, an int property will be + * returned as an Integer, a double as a Double, etc. + * + * @param name the name of the property. + * @return the property, or null if the property doesn't exist. + */ + public synchronized Object getProperty(String name) { + return properties.get(name); + } + + /** + * Sets a packet property with an int value. + * + * @param name the name of the property. + * @param value the value of the property. + */ + public void setProperty(String name, int value) { + setProperty(name, new Integer(value)); + } + + /** + * Sets a packet property with a long value. + * + * @param name the name of the property. + * @param value the value of the property. + */ + public void setProperty(String name, long value) { + setProperty(name, new Long(value)); + } + + /** + * Sets a packet property with a float value. + * + * @param name the name of the property. + * @param value the value of the property. + */ + public void setProperty(String name, float value) { + setProperty(name, new Float(value)); + } + + /** + * Sets a packet property with a double value. + * + * @param name the name of the property. + * @param value the value of the property. + */ + public void setProperty(String name, double value) { + setProperty(name, new Double(value)); + } + + /** + * Sets a packet property with a bboolean value. + * + * @param name the name of the property. + * @param value the value of the property. + */ + public void setProperty(String name, boolean value) { + setProperty(name, new Boolean(value)); + } + + /** + * Sets a property with an Object as the value. The value must be Serializable + * or an IllegalArgumentException will be thrown. + * + * @param name the name of the property. + * @param value the value of the property. + */ + public synchronized void setProperty(String name, Object value) { + if (!(value instanceof Serializable)) { + throw new IllegalArgumentException("Value must be serialiazble"); + } + properties.put(name, value); + } + + /** + * Deletes a property. + * + * @param name the name of the property to delete. + */ + public synchronized void deleteProperty(String name) { + properties.remove(name); + } + + /** + * Returns an Iterator for all the property names that are set. + * + * @return an Iterator for all property names. + */ + public synchronized Iterator getPropertyNames() { + return properties.keySet().iterator(); + } + + /** + * Returns the packet as XML. Every concrete extension of Packet must implement + * this method. In addition to writing out packet-specific data, each extension should + * also write out the error and the properties data if they are defined. + * + * @return the XML format of the packet as a String. + */ + public abstract String toXML(); + + /** + * Returns the properties portion of the packet as XML or null if there are + * no properties. + * + * @return the properties data as XML or null if there are no properties. + */ + protected synchronized String getPropertiesXML() { + // Return null if there are no properties. + if (properties.isEmpty()) { + return null; + } + StringBuffer buf = new StringBuffer(); + buf.append(""); + // Loop through all properties and write them out. + for (Iterator i=getPropertyNames(); i.hasNext(); ) { + String name = (String)i.next(); + Object value = getProperty(name); + buf.append(""); + buf.append("").append(StringUtils.escapeForXML(name)).append(""); + buf.append("").append(value).append(""); + } + else if (value instanceof Long) { + buf.append("long\">").append(value).append(""); + } + else if (value instanceof Float) { + buf.append("float\">").append(value).append(""); + } + else if (value instanceof Double) { + buf.append("double\">").append(value).append(""); + } + else if (value instanceof Boolean) { + buf.append("boolean\">").append(value).append(""); + } + else if (value instanceof String) { + buf.append("string\">"); + buf.append(StringUtils.escapeForXML((String)value)); + buf.append(""); + } + // Otherwise, it's a generic Serializable object. Serialized objects are in + // a binary format, which won't work well inside of XML. Therefore, we base-64 + // encode the binary data before adding it to the SOAP payload. + else { + try { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(byteStream); + out.writeObject(value); + String encodedVal = StringUtils.encodeBase64(byteStream.toByteArray()); + buf.append("java-object\">"); + buf.append(encodedVal).append(""); + } + buf.append(""); + return buf.toString(); + } +} diff --git a/source/org/jivesoftware/smack/packet/Presence.java b/source/org/jivesoftware/smack/packet/Presence.java new file mode 100644 index 000000000..4e0bd7cbb --- /dev/null +++ b/source/org/jivesoftware/smack/packet/Presence.java @@ -0,0 +1,263 @@ +/** + * $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.packet; + +import org.jivesoftware.smack.*; + +/** + * Represents XMPP presence packets. Each packet will either be an "available" or + * "unavailable" message. A number of attributes are optional: + *
    + *
  • Status -- free-form text describing a user's presence (i.e., gone to lunch). + *
  • Priority -- non-negative numerical priority of a sender's resource. The + * highest resource priority is the default recipient of packets not addressed + * to a particular resource. + *
  • Mode -- one of four presence modes: chat, away, xa (extended away, and + * dnd (do not disturb). + *
+ * + * Note: XMPP presence packets are also used to subscribe and unsubscribe users from + * rosters. However, that functionality is controlled by the Roster object rather + * than Presence objects. + * + * @author Matt Tucker + */ +public class Presence extends Packet { + + public static final Mode CHAT = new Mode("chat"); + public static final Mode AWAY = new Mode("away"); + public static final Mode EXTENDED_AWAY = new Mode("xa"); + public static final Mode DO_NOT_DISTURB = new Mode("dnd"); + public static final Mode INVISIBLE = new Mode("invisible"); + + private boolean available = true; + private String status = null; + private int priority = -1; + private Mode mode = null; + + /** + * Creates a new presence update. Status, priority, and mode are left un-set. + * + * @param available true if this is an "available" presence packet, or false for + * "unavailable". + */ + public Presence(boolean available) { + this.available = available; + } + + /** + * Creates a new presence update with a specified status, priority, and mode. + * + * @param available true if this is an "available" presence update, or false for + * "unavailable". + * @param status a text message describing the presence update. + * @param priority the priority of this presence update. + * @param mode the mode type for this presence update. + */ + public Presence(boolean available, String status, int priority, Mode mode) { + this.available = available; + this.status = status; + this.priority = priority; + this.mode = mode; + } + + /** + * Returns true if this is an "available" presence update and false if "unavailable". + * + * @return true if an "available" presence update, false otherwise. + */ + public boolean isAvailable() { + return available; + } + + /** + * Toggles the presence update between "available" and "unavailable". + * + * @param available true to make this an "available" presence update, or false + * for "unavailable". + */ + public void setAvailable(boolean available) { + this.available = available; + } + + /** + * Returns the status message of the presence update, or null if there + * is not status. The status is free-form text describing a user's presence + * (i.e., "gone to lunch"). + * + * @return the status message. + */ + public String getStatus() { + return status; + } + + /** + * Sets the status message of the presence update. The status is free-form text + * describing a user's presence (i.e., gone to lunch). + * + * @param status the status message. + */ + public void setStatus(String status) { + this.status = status; + } + + /** + * Returns the priority of the presence, or -1 if no priority has been set. + * + * @return the priority. + */ + public int getPriority() { + return priority; + } + + /** + * Sets the priority of the presence. + * + * @param priority the priority of the presence. + */ + public void setPriority(int priority) { + this.priority = priority; + } + + /** + * Returns the mode of the presence update, or null if no mode has been set. + * + * @return the mode. + */ + public Mode getMode() { + return mode; + } + + /** + * Sets the mode of the presence update. + * + * @param mode the mode. + */ + public void setMode(Mode mode) { + this.mode = mode; + } + + public String toXML() { + StringBuffer buf = new StringBuffer(); + buf.append(""); + } + else { + buf.append("type=\"unavailable\">"); + } + if (status != null) { + buf.append("").append(status).append(""); + } + if (priority != -1) { + buf.append("").append(priority).append(""); + } + if (mode != null) { + buf.append("").append(mode).append(""); + } + buf.append(""); + return buf.toString(); + } + + /** + * A typsafe enum class to represent the presecence mode. + */ + public static class Mode { + + private String value; + + private Mode(String value) { + this.value = value; + } + + public String toString() { + return value; + } + + /** + * Returns the Mode constant + */ + public static Mode fromString(String value) { + if (value == null) { + return null; + } + else if (value.equals("chat")) { + return CHAT; + } + else if (value.equals("away")) { + return AWAY; + } + else if (value.equals("xa")) { + return EXTENDED_AWAY; + } + else if (value.equals("dnd")) { + return DO_NOT_DISTURB; + } + else if (value.equals("invisible")) { + return INVISIBLE; + } + else { + return null; + } + } + } +} diff --git a/source/org/jivesoftware/smack/util/StringUtils.java b/source/org/jivesoftware/smack/util/StringUtils.java new file mode 100644 index 000000000..1dfd6a357 --- /dev/null +++ b/source/org/jivesoftware/smack/util/StringUtils.java @@ -0,0 +1,364 @@ +/** + * $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.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.io.UnsupportedEncodingException; +import java.util.Random; + +/** + * A collection of utility methods for String objects. + */ +public class StringUtils { + + private static final char[] QUOTE_ENCODE = """.toCharArray(); + private static final char[] AMP_ENCODE = "&".toCharArray(); + private static final char[] LT_ENCODE = "<".toCharArray(); + private static final char[] GT_ENCODE = ">".toCharArray(); + + /** + * Escapes all necessary characters in the String so that it can be used + * in an XML doc. + * + * @param string the string to escape. + * @return the string with appropriate characters escaped. + */ + public static final String escapeForXML(String string) { + if (string == null) { + return null; + } + char ch; + int i=0; + int last=0; + char[] input = string.toCharArray(); + int len = input.length; + StringBuffer out = new StringBuffer((int)(len*1.3)); + for (; i < len; i++) { + ch = input[i]; + if (ch > '>') { + continue; + } + else if (ch == '<') { + if (i > last) { + out.append(input, last, i - last); + } + last = i + 1; + out.append(LT_ENCODE); + } + else if (ch == '>') { + if (i > last) { + out.append(input, last, i - last); + } + last = i + 1; + out.append(GT_ENCODE); + } + + else if (ch == '&') { + if (i > last) { + out.append(input, last, i - last); + } + last = i + 1; + out.append(AMP_ENCODE); + } + else if (ch == '"') { + if (i > last) { + out.append(input, last, i - last); + } + last = i + 1; + out.append(QUOTE_ENCODE); + } + } + if (last == 0) { + return string; + } + if (i > last) { + out.append(input, last, i - last); + } + return out.toString(); + } + + /** + * Used by the hash method. + */ + private static MessageDigest digest = null; + + /** + * Hashes a String using the SHA-1 algorithm and returns the result as a + * String of hexadecimal numbers. This method is synchronized to avoid + * excessive MessageDigest object creation. If calling this method becomes + * a bottleneck in your code, you may wish to maintain a pool of + * MessageDigest objects instead of using this method. + *

+ * 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