Commit ee43a017 authored by Matt Tucker's avatar Matt Tucker Committed by matt

Removed use of UnauthorizedException, cleaned up connection logic, start of...

Removed use of UnauthorizedException, cleaned up connection logic, start of work on TLS, Javadoc improvements.


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@1200 b35dd754-fafc-0310-a699-88a17e54d16e
parent 576e8962
......@@ -11,8 +11,8 @@
package org.jivesoftware.messenger;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.xmpp.packet.Packet;
import org.jivesoftware.messenger.auth.UnauthorizedException;
/**
* Interface to handle packets delivered by Channels.
......@@ -25,8 +25,7 @@ public interface ChannelHandler<T extends Packet> {
* Process an XMPP packet.
*
* @param packet a packet to process.
* @throws UnauthorizedException thrown if the packet's sender lacks authorization
* to access resources (will result in uniform unauthorized access error reply).
* @throws UnauthorizedException if not allowed to process the packet.
* @throws PacketException thrown if the packet is malformed (results in the sender's
* session being shutdown).
*/
......
......@@ -16,6 +16,7 @@ import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.net.SocketConnection;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.xmpp.packet.JID;
......@@ -37,6 +38,13 @@ public class ClientSession extends Session {
private static final String ETHERX_NAMESPACE = "http://etherx.jabber.org/streams";
private static final String FLASH_NAMESPACE = "http://www.jabber.com/streams/flash";
private static final String TLS_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-tls";
/**
* Version of the XMPP spec supported as MAJOR_VERSION.MINOR_VERSION (e.g. 1.0).
*/
private static final int MAJOR_VERSION = 0;
private static final int MINOR_VERSION = 0;
/**
* The authentication token for this session.
......@@ -53,9 +61,9 @@ public class ClientSession extends Session {
private int conflictCount = 0;
/**
* Returns a newly created session between the server and a client. The session will be created
* and returned only if correct Name/Prefix (i.e. 'stream' or 'flash') and Namespace were
* provided by the client.
* Returns a newly created session between the server and a client. The session will
* be created and returned only if correct name/prefix (i.e. 'stream' or 'flash')
* and namespace were provided by the client.
*
* @param serverName the name of the server where the session is connecting to.
* @param reader the reader that is reading the provided XML through the connection.
......@@ -63,37 +71,73 @@ public class ClientSession extends Session {
* @return a newly created session between the server and a client.
*/
public static Session createSession(String serverName, XPPPacketReader reader,
Connection connection) throws XmlPullParserException, UnauthorizedException,
IOException {
SocketConnection connection) throws XmlPullParserException, UnauthorizedException,
IOException
{
XmlPullParser xpp = reader.getXPPParser();
Session session;
boolean isFlashClient = xpp.getPrefix().equals("flash");
connection.setFlashClient(isFlashClient);
// Conduct error checking, the opening tag should be 'stream'
// in the 'etherx' namespace
if (!xpp.getName().equals("stream") && !isFlashClient) {
throw new XmlPullParserException(LocaleUtils.getLocalizedString("admin.error.bad-stream"));
throw new XmlPullParserException(
LocaleUtils.getLocalizedString("admin.error.bad-stream"));
}
if (!xpp.getNamespace(xpp.getPrefix()).equals(ETHERX_NAMESPACE) &&
!(isFlashClient && xpp.getNamespace(xpp.getPrefix()).equals(FLASH_NAMESPACE))) {
throw new XmlPullParserException(LocaleUtils.getLocalizedString("admin.error.bad-namespace"));
!(isFlashClient && xpp.getNamespace(xpp.getPrefix()).equals(FLASH_NAMESPACE)))
{
throw new XmlPullParserException(LocaleUtils.getLocalizedString(
"admin.error.bad-namespace"));
}
// Create a ClientSession for this user
session = SessionManager.getInstance().createClientSession(connection);
// TODO Should we keep the language requested by the client in the session so that
// future messages to the client may use the correct resource bundle? So far we are
// only answering the same language specified by the client (if any) or if none then
// answer a default language
// Default language is English ("en").
String language = "en";
// Default to a version of "0.0". Clients written before the XMPP 1.0 spec may
// not report a version in which case "0.0" should be assumed (per rfc3920
// section 4.4.1).
int majorVersion = 0;
int minorVersion = 0;
for (int i = 0; i < xpp.getAttributeCount(); i++) {
if ("lang".equals(xpp.getAttributeName(i))) {
language = xpp.getAttributeValue(i);
}
if ("version".equals(xpp.getAttributeName(i))) {
try {
String [] versionString = xpp.getAttributeValue(i).split("\\.");
majorVersion = Integer.parseInt(versionString[0]);
minorVersion = Integer.parseInt(versionString[1]);
}
catch (Exception e) {
Log.error(e);
}
}
}
// If the client supports a greater major version than the server,
// set the version to the highest one the server supports.
if (majorVersion > MAJOR_VERSION) {
majorVersion = MAJOR_VERSION;
minorVersion = MINOR_VERSION;
}
else if (majorVersion == MAJOR_VERSION) {
// If the client supports a greater minor version than the
// server, set the version to the highest one that the server
// supports.
if (minorVersion > MINOR_VERSION) {
minorVersion = MINOR_VERSION;
}
}
// Store language and version information in the connection.
connection.setLanaguage(language);
connection.setXMPPVersion(majorVersion, minorVersion);
// Create a ClientSession for this user.
Session session = SessionManager.getInstance().createClientSession(connection);
Writer writer = connection.getWriter();
// Build the start packet response
StringBuilder sb = new StringBuilder();
......@@ -112,18 +156,58 @@ public class ClientSession extends Session {
sb.append(session.getStreamID().toString());
sb.append("\" xml:lang=\"");
sb.append(language);
// Don't include version info if the version is 0.0.
if (majorVersion != 0) {
sb.append("\" version=\"");
sb.append(majorVersion).append(".").append(minorVersion);
}
sb.append("\">");
writer.write(sb.toString());
// If this is a flash client then flag the connection and append a special caracter
// to the response
// If this is a "Jabber" connection, the session is now initialized and we can
// return to allow normal packet parsing.
if (majorVersion == 0) {
// If this is a flash client append a special caracter to the response.
if (isFlashClient) {
writer.write('\0');
}
writer.flush();
return session;
}
// Otherwise, this is at least XMPP 1.0 so we need to announce stream features.
sb = new StringBuilder();
sb.append("<stream:features>");
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
// sb.append("<required/>");
sb.append("</starttls></stream:features>");
writer.write(sb.toString());
if (isFlashClient) {
session.getConnection().setFlashClient(true);
writer.write('\0');
}
writer.flush();
// TODO: check for SASL support in opening stream tag
boolean done = false;
while (!done) {
if (xpp.next() == XmlPullParser.START_TAG) {
done = true;
if (xpp.getName().equals("starttls") &&
xpp.getNamespace(xpp.getPrefix()).equals(TLS_NAMESPACE))
{
writer.write("<proceed xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
if (isFlashClient) {
writer.write('\0');
}
writer.flush();
// TODO: setup SSLEngine and negotiate TLS.
}
}
}
return session;
}
......@@ -161,11 +245,13 @@ public class ClientSession extends Session {
* status to authenticated and enables many features that are not
* available until authenticated (obtaining managers for example).
*
* @param auth The authentication token obtained from the AuthFactory
* @param resource The resource this session authenticated under
* @param userManager The user manager this authentication occured under
* @param auth the authentication token obtained from the AuthFactory.
* @param resource the resource this session authenticated under.
* @param userManager the user manager this authentication occured under.
*/
public void setAuthToken(AuthToken auth, UserManager userManager, String resource) throws UserNotFoundException {
public void setAuthToken(AuthToken auth, UserManager userManager, String resource)
throws UserNotFoundException
{
User user = userManager.getUser(auth.getUsername());
setAddress(new JID(user.getUsername(), getServerName(), resource));
authToken = auth;
......@@ -186,9 +272,9 @@ public class ClientSession extends Session {
}
/**
* <p>Obtain the authentication token associated with this session.</p>
* Returns the authentication token associated with this session.
*
* @return The authentication token associated with this session (can be null)
* @return the authentication token associated with this session (can be null).
*/
public AuthToken getAuthToken() {
return authToken;
......@@ -289,15 +375,7 @@ public class ClientSession extends Session {
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
// TODO: Should attempt to do something with the packet
try {
conn.close();
}
catch (UnauthorizedException e1) {
// TODO: something more intelligent, if the connection is
// already closed this will throw an exception but it is not a
// logged error
Log.error(LocaleUtils.getLocalizedString("admin.error"), e1);
}
conn.close();
}
}
}
......
......@@ -178,7 +178,7 @@ public class ComponentSession extends Session {
super(serverName, conn, id);
}
public void process(Packet packet) throws UnauthorizedException, PacketException {
public void process(Packet packet) throws PacketException {
// Since ComponentSessions are not being stored in the RoutingTable this messages is very
// unlikely to be sent
component.processPacket(packet);
......@@ -209,12 +209,7 @@ public class ComponentSession extends Session {
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
try {
conn.close();
}
catch (UnauthorizedException e1) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e1);
}
conn.close();
}
}
}
......
......@@ -11,9 +11,8 @@
package org.jivesoftware.messenger;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.xmpp.packet.Packet;
import org.dom4j.io.XMLWriter;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import java.net.InetAddress;
import java.net.UnknownHostException;
......@@ -27,136 +26,141 @@ import java.io.Writer;
public interface Connection {
/**
* <p>Verify that the connection is still live.</p>
* <p>Typically this is done by sending a whitespace character between packets.</p>
* Verifies that the connection is still live. Typically this is done by
* sending a whitespace character between packets.
*
* @return True if the socket remains valid, false otherwise
* @return true if the socket remains valid, false otherwise.
*/
boolean validate();
public boolean validate();
/**
* Initializes the connection with it's owning session. Allows the
* connection class to configure itself with session related information
* (e.g. stream ID).
*
* @param session The session that owns this connection
*/
void init(Session session);
/**
* <p>Obtain the InetAddress describing the connection.</p>
*
* @return The InetAddress describing the underlying connection properties
* @throws UnauthorizedException If caller doesn't have permission to
* access this resource
* @param session the session that owns this connection
*/
InetAddress getInetAddress() throws UnauthorizedException, UnknownHostException;
public void init(Session session);
/**
* <p>Obtain the XmlSerializer used to send data to the client.</p>
* <P>The serializer should only be used to obtain information about the
* serialization and should not be written to directly. Other threads maybe
* trying to write to the serializer so it is important that all writes are
* properly synchronized.</p>
* Returns the InetAddress describing the connection.
*
* @return The XmlSerializer underlying this connection
* @throws UnauthorizedException If caller doesn't have permission to access this resource
* @return the InetAddress describing the underlying connection properties.
*/
XMLWriter getSerializer() throws UnauthorizedException;
public InetAddress getInetAddress() throws UnknownHostException;
/**
* <p>Obtain the Writer used to send data to the client.</p>
* <P>The writer should be used with caution.</p>
*
* @return The Writer underlying this connection
* @throws UnauthorizedException If caller doesn't have permission to access this resource
*/
Writer getWriter() throws UnauthorizedException;
* Returns the Writer used to send data to the connection. The writer should be
* used with caution. In the majority of cases, the {@link #deliver(Packet)}
* method should be used to send data instead of using the writer directly.
* You must synchronize on the writer before writing data to it to ensure
* data consistency:
*
* <pre>
* Writer writer = connection.getWriter();
* synchronized(writer) {
* // write data....
* }</pre>
*
* @return the Writer for this connection.
*/
public Writer getWriter();
/**
* Close this session including associated socket connection.
* <p/>
* Any selector registrations (if using nio) are also removed.
* The order of events for closing the session is:
* Close this session including associated socket connection. The order of
* events for closing the session is:
* <ul>
* <li>set closing flag to prevent redundant shutdowns
* <li>notifyEvent all listeners that the channel is shutting down
* <li>close the socket
* <li>Set closing flag to prevent redundant shutdowns.
* <li>Call notifyEvent all listeners that the channel is shutting down.
* <li>Close the socket.
* </ul>
*
* @throws UnauthorizedException If caller doesn't have permission to access this resource
*/
void close() throws UnauthorizedException;
public void close();
/**
* Retrieve the closed state of the Session.
* Returns true if the connection/session is closed.
*
* @return True if the session is closed
* @return true if the connection is closed.
*/
boolean isClosed();
public boolean isClosed();
/**
* <p>Determines if this connection is secure.</p>
* Returns true if this connection is secure.
*
* @return True if the connection is secure (e.g. SSL/TLS)
* @return true if the connection is secure (e.g. SSL/TLS)
*/
boolean isSecure();
public boolean isSecure();
/**
* Register a listener for close event notification. Registrations after
* Registers a listener for close event notification. Registrations after
* the Session is closed will be immediately notified <em>before</em>
* the registration call returns (within the context of the
* registration call). An optional handback object can be associated with
* the registration if the same listener is registered to listen for multiple
* connection closures.
*
* @param listener The listener to register for events
* @param handbackMessage The object to send in the event notification
* @return The message previously registered for this channel or null if no registration existed
* @throws UnauthorizedException If caller doesn't have permission to access this resource
* @param listener the listener to register for events.
* @param handbackMessage the object to send in the event notification.
* @return the message previously registered for this channel or <tt>null</tt>
* if no registration existed
*/
Object registerCloseListener(ConnectionCloseListener listener, Object handbackMessage) throws UnauthorizedException;
public Object registerCloseListener(ConnectionCloseListener listener, Object handbackMessage);
/**
* 9
* Remove a registered close event listener. Registered listeners must
* Removes a registered close event listener. Registered listeners must
* be able to receive close events up until the time this method returns.
* (i.e. It is possible to call unregister, receive a close event registration,
* (i.e. it is possible to call unregister, receive a close event registration,
* and then have the unregister call return.)
*
* @param listener The listener to deregister for close events
* @return The Message registered with this listener or null if the channel was never registered
* @throws UnauthorizedException If caller doesn't have permission to access this resource
* @param listener the listener to deregister for close events.
* @return the Message registered with this listener or <tt>null</tt> if the
* channel was never registered.
*/
Object removeCloseListener(ConnectionCloseListener listener) throws UnauthorizedException;
public Object removeCloseListener(ConnectionCloseListener listener);
/**
* Delivers the packet to this XMPPAddress without checking the recipient.
* The method essentially calls
* <code>socket.send(packet.getWriteBuffer())</code>
* Delivers the packet to this connection without checking the recipient.
* The method essentially calls <code>socket.send(packet.getWriteBuffer())</code>.
*
* @param packet The packet to deliver.
* @throws UnauthorizedException If caller doesn't have permission to access this resource
* @param packet the packet to deliver.
*/
void deliver(Packet packet) throws UnauthorizedException;
public void deliver(Packet packet) throws UnauthorizedException;
/**
* Sets whether the connected client is a flash client or not. Flash clients need to receive
* a special character (i.e. \0) at the end of each xml packet. Flash clients may send the
* character \0 in incoming packets and may start a connection using another openning tag
* such as: "flash:client".
* Returns true if the connected client is a flash client. Flash clients need
* to receive a special character (i.e. \0) at the end of each xml packet. Flash
* clients may send the character \0 in incoming packets and may start a connection
* using another openning tag such as: "flash:client".
*
* @param flashClient flag that indicates if the client is a flash client.
* @return true if the connected client is a flash client.
*/
void setFlashClient(boolean flashClient);
public boolean isFlashClient();
/**
* Returns true if the connected client is a flash client. Flash clients need to receive
* a special character (i.e. \0) at the end of each xml packet. Flash clients may send the
* character \0 in incoming packets and may start a connection using another openning tag
* such as: "flash:client".
* Returns the major version of XMPP being used by this connection
* (major_version.minor_version. In most cases, the version should be
* "1.0". However, older clients using the "Jabber" protocol do not set a
* version. In that case, the version is "0.0".
*
* @return true if the connected client is a flash client.
* @return the major XMPP version being used by this connection.
*/
public int getMajorXMPPVersion();
/**
* Returns the minor version of XMPP being used by this connection
* (major_version.minor_version. In most cases, the version should be
* "1.0". However, older clients using the "Jabber" protocol do not set a
* version. In that case, the version is "0.0".
*
* @return the minor XMPP version being used by this connection.
*/
public int getMinorXMPPVersion();
/**
* Returns the language code that should be used for this connection
* (e.g. "en").
*
* @return the language code for the connection.
*/
boolean isFlashClient();
public String getLanguage();
}
......@@ -16,23 +16,25 @@ import java.util.Iterator;
import org.xmlpull.v1.XmlPullParserException;
/**
* <p>Coordinates connections (accept, read, termination) on the server.</p>
* Coordinates connections (accept, read, termination) on the server.
*
* @author Iain Shigeoka
*/
public interface ConnectionManager {
/**
* <p>Obtain an array of the ports managed by this connection manager.</p>
* Returns an array of the ports managed by this connection manager.
*
* @return Iterator of the ports managed by this connection manager (can be an empty but never null)
* @return an iterator of the ports managed by this connection manager
* (can be an empty but never null).
*/
public Iterator<ServerPort> getPorts();
/**
* <p>Adds a socket to be managed by the connection manager.</p>
* Adds a socket to be managed by the connection manager.
*
* @param sock The socket to add to this manager for management
* @param isSecure True if this is a secure connection
* @param socket the socket to add to this manager for management.
* @param isSecure true if the connection is secure.
*/
public void addSocket(Socket sock, boolean isSecure) throws XmlPullParserException;
public void addSocket(Socket socket, boolean isSecure) throws XmlPullParserException;
}
......@@ -12,7 +12,6 @@
package org.jivesoftware.messenger;
import org.dom4j.Element;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.messenger.handler.IQHandler;
import org.jivesoftware.util.LocaleUtils;
......@@ -215,33 +214,23 @@ public class IQRouter extends BasicModule {
Log.info("Packet sent to unreachable address " + packet);
Session session = sessionManager.getSession(packet.getFrom());
if (session != null) {
try {
IQ reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.service_unavailable);
session.getConnection().deliver(reply);
}
catch (UnauthorizedException ex) {
Log.error(LocaleUtils.getLocalizedString("admin.error.routing"), e);
}
IQ reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.service_unavailable);
session.getConnection().deliver(reply);
}
}
}
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.routing"), e);
try {
Session session = sessionManager.getSession(packet.getFrom());
if (session != null) {
Connection conn = session.getConnection();
if (conn != null) {
conn.close();
}
Session session = sessionManager.getSession(packet.getFrom());
if (session != null) {
Connection conn = session.getConnection();
if (conn != null) {
conn.close();
}
}
catch (UnauthorizedException e1) {
// do nothing
}
}
}
......
......@@ -255,7 +255,7 @@ public class InternalComponentManager implements ComponentManager, RoutableChann
*
* @param packet the packet to process.
*/
public void process(Packet packet) throws UnauthorizedException, PacketException {
public void process(Packet packet) throws PacketException {
Component component = getComponent(packet.getFrom().getDomain());
// Only process packets that were sent by registered components
if (component != null) {
......@@ -293,7 +293,7 @@ public class InternalComponentManager implements ComponentManager, RoutableChann
return jid;
}
public void process(Packet packet) throws UnauthorizedException, PacketException {
public void process(Packet packet) throws PacketException {
component.processPacket(packet);
}
}
......
......@@ -11,8 +11,8 @@
package org.jivesoftware.messenger;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.xmpp.packet.Packet;
import org.jivesoftware.messenger.auth.UnauthorizedException;
/**
* Delivers packets to locally connected streams. This is the opposite
......@@ -29,8 +29,8 @@ public interface PacketDeliverer {
* Be careful to enforce concurrency DbC of concurrent by synchronizing
* any accesses to class resources.
*
* @param packet The packet to route
* @throws java.lang.NullPointerException If the packet is null or the packet could not be routed
* @param packet the packet to route
* @throws PacketException if the packet is null or the packet could not be routed.
*/
public void deliver(Packet packet) throws UnauthorizedException, PacketException;
}
......@@ -112,18 +112,13 @@ public class PresenceRouter extends BasicModule {
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.routing"), e);
try {
Session session = sessionManager.getSession(packet.getFrom());
if (session != null) {
Connection conn = session.getConnection();
if (conn != null) {
conn.close();
}
Session session = sessionManager.getSession(packet.getFrom());
if (session != null) {
Connection conn = session.getConnection();
if (conn != null) {
conn.close();
}
}
catch (UnauthorizedException e1) {
// do nothing
}
}
}
......
......@@ -36,8 +36,8 @@ public interface DiscoItemsProvider {
* case that the sender of the disco request is not authorized to discover items an
* UnauthorizedException will be thrown.
*
* @param name the recipient JID's name.
* @param node the requested disco node.
* @param name the recipient JID's name.
* @param node the requested disco node.
* @param senderJID the XMPPAddress of user that sent the disco items request.
* @return an Iterator (of Element) with the target entity's items or null if none.
* @throws UnauthorizedException if the senderJID is not authorized to discover items.
......
......@@ -246,5 +246,4 @@ public class IQDiscoItemsHandler extends IQHandler implements ServerFeaturesProv
};
return discoItemsProvider;
}
}
}
\ No newline at end of file
......@@ -42,7 +42,7 @@ public abstract class IQHandler extends BasicModule implements ChannelHandler {
super(moduleName);
}
public void process(Packet packet) throws UnauthorizedException, PacketException {
public void process(Packet packet) throws PacketException {
IQ iq = (IQ) packet;
try {
iq = handleIQ(iq);
......
......@@ -18,7 +18,6 @@ import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.roster.Roster;
import org.jivesoftware.messenger.roster.RosterItem;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.*;
import org.xmpp.packet.Presence;
import org.xmpp.packet.Packet;
......@@ -82,7 +81,7 @@ public class PresenceSubscribeHandler extends BasicModule implements ChannelHand
super("Presence subscription handler");
}
public void process(Packet xmppPacket) throws UnauthorizedException, PacketException {
public void process(Packet xmppPacket) throws PacketException {
Presence presence = (Presence)xmppPacket;
try {
JID senderJID = presence.getFrom();
......
......@@ -62,21 +62,14 @@ public class SSLSocketAcceptThread extends Thread {
* Creates an instance using the default port, TLS transport security, and
* JVM defaults for all security settings.
*
* @param connManager The connection manager that will manage connections generated by this thread
* @throws IOException If there was trouble initializing the SSL configuration
* @param connManager the connection manager that will manage connections
* generated by this thread
* @throws IOException if there was trouble initializing the SSL configuration.
*/
public SSLSocketAcceptThread(ConnectionManager connManager)
throws IOException {
super("SSL accept");
public SSLSocketAcceptThread(ConnectionManager connManager) throws IOException {
super("Secure Socket Listener");
this.connManager = connManager;
int port = SSLSocketAcceptThread.DEFAULT_PORT;
String portName = JiveGlobals.getProperty("xmpp.socket.ssl.port");
if (portName != null) {
int portValue = Integer.parseInt(portName);
if (portValue > 0) {
port = Integer.parseInt(portName);
}
}
int port = JiveGlobals.getIntProperty("xmpp.socket.ssl.port", DEFAULT_PORT);
String interfaceName = JiveGlobals.getProperty("xmpp.socket.ssl.interface");
bindInterface = null;
......
......@@ -57,7 +57,7 @@ public class SocketAcceptThread extends Thread {
private ConnectionManager connManager;
public SocketAcceptThread(ConnectionManager connManager) {
super("SAT accept");
super("Socket Listener");
this.connManager = connManager;
port = JiveGlobals.getIntProperty("xmpp.socket.plain.port", DEFAULT_PORT);
String interfaceName = JiveGlobals.getProperty("xmpp.socket.plain.interface");
......
......@@ -12,13 +12,10 @@
package org.jivesoftware.messenger.net;
import org.dom4j.io.XMLWriter;
import org.jivesoftware.messenger.PacketDeliverer;
import org.jivesoftware.messenger.PacketException;
import org.jivesoftware.messenger.Session;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.interceptor.InterceptorManager;
import org.jivesoftware.messenger.interceptor.PacketRejectedException;
import org.jivesoftware.messenger.spi.BasicConnection;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.xmpp.packet.Packet;
......@@ -29,48 +26,47 @@ import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
/**
* An object to track the state of a Jabber client-server session.
* An object to track the state of a XMPP client-server session.
* Currently this class contains the socket channel connecting the
* client and server.
*
* @author Iain Shigeoka
*/
public class SocketConnection extends BasicConnection {
public class SocketConnection implements Connection {
/**
* The socket this session represents
*/
private Socket sock;
private Map listeners = new HashMap();
private Socket socket;
/**
* The utf-8 charset for decoding and encoding Jabber packet streams.
* The utf-8 charset for decoding and encoding XMPP packet streams.
*/
private String charset = "UTF-8";
/**
* The writer used to send outgoing data.
*/
private Writer writer;
/**
* The packet deliverer for local packets
*/
private PacketDeliverer deliverer;
private Session session;
private boolean secure;
private XMLWriter xmlSerializer;
private boolean flashClient = false;
private int majorVersion = 1;
private int minorVersion = 0;
private String language = null;
/**
* Create a new session using the supplied socket.
*
* @param deliverer The packet deliverer this connection will use
* @param socket The socket to represent
* @param isSecure True if this is a secure connection
* @throws NullPointerException If the socket is null
* @param deliverer the packet deliverer this connection will use.
* @param socket the socket to represent.
* @param isSecure true if this is a secure connection.
* @throws NullPointerException if the socket is null.
*/
public SocketConnection(PacketDeliverer deliverer, Socket socket, boolean isSecure)
throws IOException
......@@ -80,8 +76,8 @@ public class SocketConnection extends BasicConnection {
}
this.secure = isSecure;
sock = socket;
writer = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream(), charset));
this.socket = socket;
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), charset));
this.deliverer = deliverer;
xmlSerializer = new XMLWriter(writer);
}
......@@ -107,26 +103,32 @@ public class SocketConnection extends BasicConnection {
session = owner;
}
public InetAddress getInetAddress() throws UnauthorizedException {
return sock.getInetAddress();
public Object registerCloseListener(ConnectionCloseListener listener, Object handbackMessage) {
Object status = null;
if (isClosed()) {
listener.onConnectionClose(handbackMessage);
}
else {
status = listeners.put(listener, handbackMessage);
}
return status;
}
public XMLWriter getSerializer() throws UnauthorizedException {
return xmlSerializer;
public Object removeCloseListener(ConnectionCloseListener listener) {
return listeners.remove(listener);
}
public Writer getWriter() throws UnauthorizedException {
public InetAddress getInetAddress() {
return socket.getInetAddress();
}
public Writer getWriter() {
return writer;
}
/**
* Retrieve the closed state of the Session.
*
* @return true if the session is closed.
*/
public boolean isClosed() {
if (session == null) {
return sock.isClosed();
return socket.isClosed();
}
return session.getStatus() == Session.STATUS_CLOSED;
}
......@@ -135,6 +137,56 @@ public class SocketConnection extends BasicConnection {
return secure;
}
public int getMajorXMPPVersion() {
return majorVersion;
}
public int getMinorXMPPVersion() {
return minorVersion;
}
/**
* Sets the XMPP version information. In most cases, the version should be "1.0".
* However, older clients using the "Jabber" protocol do not set a version. In that
* case, the version is "0.0".
*
* @param majorVersion the major version.
* @param minorVersion the minor version.
*/
public void setXMPPVersion(int majorVersion, int minorVersion) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
public String getLanguage() {
return language;
}
/**
* Sets the language code that should be used for this connection (e.g. "en").
*
* @param language the language code.
*/
public void setLanaguage(String language) {
this.language = language;
}
public boolean isFlashClient() {
return flashClient;
}
/**
* Sets whether the connected client is a flash client. Flash clients need to
* receive a special character (i.e. \0) at the end of each xml packet. Flash
* clients may send the character \0 in incoming packets and may start a
* connection using another openning tag such as: "flash:client".
*
* @param flashClient true if the if the connection is a flash client.
*/
public void setFlashClient(boolean flashClient) {
this.flashClient = flashClient;
}
public synchronized void close() {
if (!isClosed()) {
try {
......@@ -156,7 +208,7 @@ public class SocketConnection extends BasicConnection {
// Do nothing
}
try {
sock.close();
socket.close();
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.close")
......@@ -166,12 +218,6 @@ public class SocketConnection extends BasicConnection {
}
}
/**
* Delivers the packet to this XMPPAddress without checking the recipient.
* The method essentially calls <tt>packet.send(serializer,version)</tt>.
*
* @param packet The packet to deliver.
*/
public void deliver(Packet packet) throws UnauthorizedException, PacketException {
if (isClosed()) {
deliverer.deliver(packet);
......@@ -203,11 +249,17 @@ public class SocketConnection extends BasicConnection {
}
}
public void setFlashClient(boolean flashClient) {
this.flashClient = flashClient;
}
public boolean isFlashClient() {
return flashClient;
/**
* Notifies all close listeners that the connection has been closed.
* Used by subclasses to properly finish closing the connection.
*/
private void notifyCloseListeners() {
synchronized (listeners) {
Iterator itr = listeners.keySet().iterator();
while (itr.hasNext()) {
ConnectionCloseListener listener = (ConnectionCloseListener)itr.next();
listener.onConnectionClose(listeners.get(listener));
}
}
}
}
\ No newline at end of file
......@@ -47,9 +47,9 @@ public class SocketReadThread extends Thread {
*/
private static XmlPullParserFactory factory = null;
private Socket sock;
private Socket socket;
private Session session;
private Connection connection;
private SocketConnection connection;
private String serverName;
/**
* Router used to route incoming packets to the correct channels.
......@@ -67,21 +67,22 @@ public class SocketReadThread extends Thread {
}
}
/**
* Create dedicated read thread for this socket.
* Creates a dedicated read thread for a socket.
*
* @param router The router for sending packets that were read
* @param serverName The name of the server this socket is working for
* @param sock The socket to read from
* @param conn The connection being read
* @param router the router for sending packets that were read.
* @param serverName the name of the server this socket is working for.
* @param socket the socket to read from.
* @param connection the connection being read.
*/
public SocketReadThread(PacketRouter router, String serverName, Socket sock, Connection conn) {
public SocketReadThread(PacketRouter router, String serverName, Socket socket,
SocketConnection connection)
{
super("SRT reader");
this.serverName = serverName;
this.router = router;
this.connection = conn;
this.sock = sock;
this.connection = connection;
this.socket = socket;
}
/**
......@@ -93,7 +94,7 @@ public class SocketReadThread extends Thread {
reader = new XPPPacketReader();
reader.setXPPFactory(factory);
reader.getXPPParser().setInput(new InputStreamReader(sock.getInputStream(),
reader.getXPPParser().setInput(new InputStreamReader(socket.getInputStream(),
CHARSET));
// Read in the opening tag and prepare for packet stream
......@@ -109,8 +110,8 @@ public class SocketReadThread extends Thread {
// Normal disconnect
}
catch (SocketException se) {
// The socket was closed. The server may close the connection for several reasons (e.g.
// user requested to remove his account). Do nothing here.
// The socket was closed. The server may close the connection for several
// reasons (e.g. user requested to remove his account). Do nothing here.
}
catch (XmlPullParserException ie) {
// Check if the user abruptly cut the connection without sending previously an
......@@ -131,9 +132,9 @@ public class SocketReadThread extends Thread {
}
}
// It is normal for clients to abruptly cut a connection
// rather than closing the stream document
// Since this is normal behavior, we won't log it as an error
// Log.error(LocaleUtils.getLocalizedString("admin.disconnect"),ie);
// rather than closing the stream document. Since this is
// normal behavior, we won't log it as an error.
// Log.error(LocaleUtils.getLocalizedString("admin.disconnect"),ie);
}
catch (Exception e) {
if (session != null) {
......@@ -152,31 +153,26 @@ public class SocketReadThread extends Thread {
}
catch (Exception e) {
Log.warn(LocaleUtils.getLocalizedString("admin.error.connection")
+ "\n" + sock.toString());
+ "\n" + socket.toString());
}
}
else {
Log.error(LocaleUtils.getLocalizedString("admin.error.connection")
+ "\n" + sock.toString());
+ "\n" + socket.toString());
}
}
}
/**
* Read the incoming stream until it ends. Much of the reading
* will actually be done in the channel handlers as they run the
* XPP through the data. This method mostly handles the idle waiting
* for incoming data. To prevent clients from stalling channel handlers,
* a watch dog timer is used. Packets that take longer than the watch
* dog limit to read will cause the session to be closed.
* Read the incoming stream until it ends.
*/
private void readStream() throws Exception {
while (true) {
Element doc = reader.parseDocument().getRootElement();
if (doc == null) {
// Stop reading the stream since the client has sent an end of stream element and
// probably closed the connection
// Stop reading the stream since the client has sent an end of
// stream element and probably closed the connection.
return;
}
......@@ -187,7 +183,7 @@ public class SocketReadThread extends Thread {
packet = new Message(doc);
}
catch(IllegalArgumentException e) {
// The original packet contains a malformed JID so answer an error
// The original packet contains a malformed JID so answer with an error.
Message reply = new Message();
reply.setID(doc.attributeValue("id"));
reply.setTo(session.getAddress());
......@@ -298,7 +294,8 @@ public class SocketReadThread extends Thread {
}
}
else {
throw new XmlPullParserException(LocaleUtils.getLocalizedString("admin.error.packet.tag") + tag);
throw new XmlPullParserException(LocaleUtils.getLocalizedString(
"admin.error.packet.tag") + tag);
}
}
}
......
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.spi;
import org.jivesoftware.messenger.Connection;
import org.jivesoftware.messenger.ConnectionCloseListener;
import org.jivesoftware.messenger.Session;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Connection helper base class. Automates common connection management
* tasks without specific knowledge of the underlying connection provider.
*
* @author Iain Shigeoka
*/
abstract public class BasicConnection implements Connection {
private Map listeners = new HashMap();
public void init(Session session) {
}
public Object registerCloseListener(ConnectionCloseListener listener, Object handbackMessage) {
Object status = null;
if (isClosed()) {
listener.onConnectionClose(handbackMessage);
}
else {
status = listeners.put(listener, handbackMessage);
}
return status;
}
public Object removeCloseListener(ConnectionCloseListener listener) {
return listeners.remove(listener);
}
/**
* Notifies all close listeners that the connection has been closed.
* Used by subclasses to properly finish closing the connection.
*/
protected void notifyCloseListeners() {
synchronized (listeners) {
Iterator itr = listeners.keySet().iterator();
while (itr.hasNext()) {
ConnectionCloseListener listener = (ConnectionCloseListener)itr.next();
listener.onConnectionClose(listeners.get(listener));
}
}
}
}
......@@ -107,7 +107,7 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
public void addSocket(Socket sock, boolean isSecure) {
try {
// the order of these calls is critical (stupid huh?)
Connection conn = new SocketConnection(deliverer, sock, isSecure);
SocketConnection conn = new SocketConnection(deliverer, sock, isSecure);
SocketReadThread reader = new SocketReadThread(router, serverName, sock, conn);
reader.setDaemon(true);
reader.start();
......
......@@ -60,9 +60,6 @@ public class TransportHandler extends BasicModule implements ChannelHandler {
try {
deliverer.deliver(packet);
}
catch (UnauthorizedException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
catch (PacketException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment