Commit 30172460 authored by Armando Jagucki's avatar Armando Jagucki Committed by ajagucki

Ported changes between r8965 and r9138 from trunk.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/branches/pep@9139 b35dd754-fafc-0310-a699-88a17e54d16e
parent c3395014
......@@ -5,6 +5,7 @@ ant-contrib.jar | 1.0b1
ant-subdirtask.jar | Revision 1.4 (CVS)
bouncycastle.jar | JDK 1.5, 137 (bcprov-jdk15-137.jar)
cglib.jar | 2.1.3 (JMock 2.1.0)
commons-lang.jar | 2.3
commons-logging.jar | Jetty 5.1.10
commons-el.jar | Jetty 6.0.1 (1.0)
commons-httpclient.jar | 3.0
......@@ -30,9 +31,9 @@ jtds.jar | 1.2
junit.jar | 4.3.1
jzlib.jar | 1.0.7
mail.jar | 1.4.0 (JavaMail)
mina-core-1.2.0.jar | 1.1.1 https://svn.apache.org/repos/asf/mina/branches/1.1
mina-filter-compression-1.2.0.jar | 1.1.1 https://svn.apache.org/repos/asf/mina/branches/1.1
mina-filter-ssl-1.2.0.jar | 1.1.1 https://svn.apache.org/repos/asf/mina/branches/1.1
mina-core.jar | 1.1.3 (https://svn.apache.org/repos/asf/mina/branches/1.1)
mina-filter-compression.jar | 1.1.3 (https://svn.apache.org/repos/asf/mina/branches/1.1)
mina-filter-ssl.jar | 1.1.3 (https://svn.apache.org/repos/asf/mina/branches/1.1)
mysql.jar | 3.1.13
objenesis | 1.0 (JMock 2.1.0)
pack200task.jar | August 5, 2004
......
......@@ -453,6 +453,26 @@
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/merge/jetty-sslengine.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/merge/commons-lang.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MODULE_DIR$/../lib/src/commons-lang-sources.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntryProperties />
</component>
<component name="VcsManagerConfiguration">
......
......@@ -31,7 +31,7 @@ messaging (IM) services using the XMPP protocol.
How to install Openfire.
</li>
<li>
<a href="http://wiki.igniterealtime.org/display/WILDFIRE/Wildfire+to+Openfire+Upgrade+Guide">Upgrade Guide</a> -
<a href="upgrade-guide.html">Upgrade Guide</a> -
Instructions for upgrading an existing Wildfire installation into an Openfire installation.
</li>
<li>
......
......@@ -21,8 +21,84 @@
<div id="pageBody">
<p>Upgrading Openfire is almost as easy as installing Openfire from scratch. As part of the upgrade process
it is highly recommended to backup your current installation and database. Having backups will let you
easily rollback to a previous state and ensure that you have not lost vital information. The rest of
the document describes the common steps to perform a successful upgrade.
</div>
The new version of Openfire will be installed over the existing version and all settings and data will be retained.
<h2>Standard Upgrade</h2>
<ul>
<h3>Windows EXE</h3>
<ol>
<li>Stop Openfire.</li>
<li>Backup the Openfire installation directory. The default is _C:\Program Files\Openfire_</li>
<li>Backup the Openfire database. Note that the embedded database is backed up in step 2.</li>
<li>Run the installer for the new version of Openfire (note that the installer MUST point to the base installation directory of the previous version).</li>
<li>Complete the installer process.</li>
<li>Start Openfire.</li>
</ol>
<h3>RPM - RedHat based unix systems (RHEL, CentOS, Fedora, etc)</h3>
<ol>
<li>Stop Openfire.</li>
<li>Backup the Openfire installation directory.</li>
<li>Backup the Openfire database. Note that the embedded database is backed up in step 2.</li>
<li>Install the new RPM. Execute <i>rpm -Uvf openfire-3.3.3-1.i386.rpm</i> to update your current install</li>
<li>Start Openfire.</li>
</ol><br>
<p>Remember that the server can be started in manual or automatic mode. Execute <i>/opt/openfire/bin/openfire.sh</i>
to start in manual mode or execute <i>/etc/init.d/openfire start</i> to start the server as a service. Execute
<i>/etc/init.d/openfire stop</i> to stop the server's service.</p>
<h4>Things to note</h4>
<ul>
<li>The new RPM installs Openfire as a service. That means that the server will be started when the OS is started and the server is stopped upon shutdown.</li>
<li>/opt/openfire/bin/openfire no longer exists. It was a start/stop script generated by install4j. Since we are no longer using install4j, it needed to go. Instead, we now have a more standard linux init script, /etc/init.d/openfire.</li>
<li>/etc/sysconfig/openfire can now be used to 'tweak' things, like paths and such. See the file for more information.</li>
<li>The entire directory tree is owned by daemon now. We ditched the need for a new user and are sticking with a standard unix system account. The RPM will take care of owning everything as you install it.</li>
<li>Beyond having /etc/init.d/openfire to stop and start openfire, it has chkconfig compatible tags in it and is automatically added via the rpm so that openfire should start up as your server starts up.</li>
<li>The RPM will no longer overwrite: conf/openfire.xml, resources/security/keystore or resources/security/truststore.</li>
</ul>
<h3>Mac OS X DMG</h3>
<ol>
<li>Stop Openfire.</li>
<li>Backup the Openfire installation directory. The default is _/usr/local/wildfire_.</li>
<li>Backup the Openfire database. Note that the embedded database is backed up in step 2.</li>
<li>Open the installer for the new version of Openfire.</li>
<li>Complete the installer process.</li>
</ol>
<h3>ZIP or TAR.GZ</h3>
<ol>
<li>Stop Openfire.</li>
<li>Backup the Openfire installation directory. This step is critical because the data will be overwritten with the new .tar.gz install.</li>
<li>Backup the Openfire database. Note that the embedded database is backed up in step 2.</li>
<li>Install the .tar.gz file (extract it over the current directory).</li>
<li>Copy the <i>conf</i> directory from the backup to the installation directory.</li>
<li>Copy the <i>embedded-db</i> directory from the backup to the installation directory.</li>
<li>Copy the <i>enterprise</i> directory from the backup to the installation directory, if it exists.</li>
<li>Copy the <i>plugins</i> directory from the backup to the installation directory except for _plugins/admin_.</li>
<li>Copy the <i>resources/security/keystore</i> file from the backup to the installation directory.</li>
<li>Copy the <i>resources/security/truststore</i> file from the backup to the installation directory if you modified this file.</li>
<li>Start Openfire</li>
</ol>
</ul>
<h2>Special Upgrade cases</h2>
There are some special occasions where some manual intervention is needed while performing the upgrade process. This is
usually the case when some mayor refactoring work was done that requires complex upgrade operations. However, this is
not a common case in Openfire (e.g. this happened twice since 2003). Below you will find the upgrade steps to follow
for these special occasions:
<ul>
<li><a href="wildfire-openfire-upgrade.html">Wildfire to Openfire Upgrade Guide</a></li>
<li><a href="wildfire-ent-3_2_0-upgrade.html">Upgrade guide to Wildfire Enterprise 3.2.0</a></li>
</ul>
</div>
</div>
......
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Upgrade guide to Wildfire Enterprise 3.2.0</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="pageContainer">
<a name="top"></a>
<div id="pageHeader">
<div id="logo"></div>
<h1>Upgrade guide to Wildfire Enterprise 3.2.0</h1>
</div>
<div class="navigation">
<a href="index.html">&laquo; Back to documentation index</a>
</div>
<div id="pageBody">
<p>As of Wildfire Enterprise 3.2.0 the enterprise plugin and Fastpath plugin were merged into
the enterprise plugin. Follow these steps to upgrade your existing installation:
<h3>For Wildfire Enterprise 3.1.0 or previous:</h3>
<ol>
<li>Upgrade to Wildfire Enterprise 3.1.1.</li>
<li>Follow the steps in the next section.</li>
</ol>
<h3>For Wildfire Enterprise 3.1.1:</h3>
<ol>
<li>Stop the server and make a backup of:</li>
<ol>
<li>the database where Wildfire is installed</li>
<li>the <i>enterprise</i> folder under <i>wildfireHome</i></li>
<li>the <i>index</i> folder under <i>wildfireHome</i></li>
<li>optional: the enterprise and fastpath plugins located in the plugins folderv
</ol>
<li>Download the latest version of Wildfire Enterprise and copy it to the <i>plugins</i> folder.</li>
<li>Start the server. The new version of the plugin will be loaded and the database will be updated.</li>
<li>Fastpath tables were replaced with new tables so you may want to <b>import existing data</b> from the old Fastpath version to the new Fastpath version. Follow these steps to import existing data:</li>
<ol>
<li>Stop the server.</li>
<li>Go to [wildfireHome]/plugins/enterprise/database/upgrade/4 to find the import plugins.</li>
<li>Execute the corresponding <i>import_*.sql</i> database script depending on your database.</li>
<li>Start the server, log into the admin console and verify that workgroup settings were imported.</li>
</ol>
</ol>
</div>
</div>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Wildfire to Openfire Upgrade Guide</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="pageContainer">
<a name="top"></a>
<div id="pageHeader">
<div id="logo"></div>
<h1>Wildfire to Openfire Upgrade Guide</h1>
</div>
<div class="navigation">
<a href="index.html">&laquo; Back to documentation index</a>
</div>
<div id="pageBody">
<p>This document provides the steps to follow in order to upgrade a Wildfire 3.2 installation into an
Openfire installation. If you need to update an old version of Wildfire then first update it to
Wildfire 3.2 following the standard steps described in the <a href="upgrade-guide.html">upgrade guide</a>
and the <a href="wildfire-ent-3_2_0-upgrade.html">Upgrade guide to Wildfire Enterprise 3.2.0</a> if you are using the Enterprise Edition.
<h3>Windows</h3>
<ol>
<li>Stop the server</li>
<li>Back up setup</li>
<li>If running as a service, remove the old service: <i>wildfire-service /uninstall</i></li>
<li>Install 3.3.0</li>
<li>Delete conf/openfire.xml and rename conf/wildfire.xml to conf/openfire.xml</li>
<li>Search for <b>org.jivesoftware.wildfire.</b> and replace with <b>org.jivesoftware.openfire.</b> in conf/openfire.xml</li>
<li>If using embedded database then rename embedded-db\wildfire\* files to embedded-db\openfire\*</li>
<li>Delete all jar files in the plugin folder and drop new versions of plugins</li>
<li>Start up the server using new launch scripts localted in bin</li>
<li>Optionally install as a service again: <i>openfire-service /install</i></li>
</ol>
<h3>Unix/Linux (RPM)</h3>
<ol>
<li>Stop the server</li>
<li>Back up setup</li>
<li>Run rpm -ivf openfire_3_3_0.rpm</li>
<li>Go to /opt/openfire and copy configuration from old setup</li>
<ol>
<li>Copy conf/wildfire.xml and rename it to conf/openfire.xml</li>
<li>Search for <b>org.jivesoftware.wildfire.</b> and replace with <b>org.jivesoftware.openfire.</b> in conf/openfire.xml</li>
<li>If using embedded database then copy and rename embedded-db\wildfire\* files to embedded-db\openfire\*</li>
<li>Copy new version of plugins to opt/openfire/plugins (except admin folder)</li>
<li>Copy old certificates from resources/security/ to same location in openfire</li>
</ol>
<li>If running as a service update service to use new scripts located in bin</li>
<li>Start up the server using new launch scripts located in bin</li>
<li>To uninstall Wildfire run rpm -e wildfire</li>
</ol><br>
<h3>Zip/Tar.gz</h3>
This process is similar to the RPM except that step 3 is replaced with:<br>
<ul>
3) Unzip content to /opt/openfire (unix/linux)<br>
3) Unzip content to C:\Program Files\openfire (windows)
</ul>
<h3>Mac OS X DMG</h3>
<ol>
<li>Install Openfire using the .pkg installer</li>
<li>Use the System Preferences pane to turn Openfire off</li>
<li>For steps 4-8 assume paths are relative to /usr/local</li>
<li>Copy wildfire/conf/wildfire.xml to openfire/conf/openfire.xml</li>
<li>Search for <b>org.jivesoftware.wildfire.</b> and replace with <b>org.jivesoftware.openfire.</b> in conf/openfire.xml</li>
<li>If using embedded database then copy and rename embedded-db\wildfire\* files to embedded-db\openfire\*</li>
<li>Copy new version of plugins to openfire/plugins (except admin folder)</li>
<li>Copy old certificates from resources/security/ to same location in openfire</li>
<li>Use the System Preferences pane to turn Openfire back on</li>
</ol>
</div>
</div>
</body>
</html>
\ No newline at end of file
......@@ -1919,7 +1919,7 @@ user.properties.registered=Registered
# User roster Page
user.roster.title=User Roster
user.roster.info=Below is the list of roster items for user {0}. Shared groups are underlined.
user.roster.info=Below is the list of roster items for user {0}. Shared groups are represented in the Groups column as underlined links to an associated group edit screen. Roster items provided by shared groups may not be deleted via this interface.
user.roster.jid=JID
user.roster.nickname=Nickname
user.roster.groups=Groups
......
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
/**
* <p>Escapes and unescapes <code>String</code>s for
* Java, Java Script, HTML, XML, and SQL.</p>
*
* @author Apache Jakarta Turbine
* @author Purple Technology
* @author <a href="mailto:alex@purpletech.com">Alexander Day Chaffee</a>
* @author Antony Riley
* @author Helge Tesgaard
* @author <a href="sean@boohai.com">Sean Brown</a>
* @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
* @author Phil Steitz
* @author Pete Gieser
* @since 2.0
* @version $Id: StringEscapeUtils.java 471626 2006-11-06 04:02:09Z bayard $
*/
public class StringEscapeUtils {
/**
* <p><code>StringEscapeUtils</code> instances should NOT be constructed in
* standard programming.</p>
*
* <p>Instead, the class should be used as:
* <pre>StringEscapeUtils.escapeJava("foo");</pre></p>
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
*/
public StringEscapeUtils() {
super();
}
/**
* <p>Escapes the characters in a <code>String</code> using JavaScript String rules.</p>
* <p>Escapes any values it finds into their JavaScript String form.
* Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
*
* <p>So a tab becomes the characters <code>'\\'</code> and
* <code>'t'</code>.</p>
*
* <p>The only difference between Java strings and JavaScript strings
* is that in JavaScript, a single quote must be escaped.</p>
*
* <p>Example:
* <pre>
* input string: He didn't say, "Stop!"
* output string: He didn\'t say, \"Stop!\"
* </pre>
* </p>
*
* @param str String to escape values in, may be null
* @return String with escaped values, <code>null</code> if null string input
*/
public static String escapeJavaScript(String str) {
return escapeJavaStyleString(str, true);
}
/**
* <p>Escapes the characters in a <code>String</code> using JavaScript String rules
* to a <code>Writer</code>.</p>
*
* <p>A <code>null</code> string input has no effect.</p>
*
* @see #escapeJavaScript(java.lang.String)
* @param out Writer to write escaped string into
* @param str String to escape values in, may be null
* @throws IllegalArgumentException if the Writer is <code>null</code>
* @throws IOException if error occurs on underlying Writer
**/
public static void escapeJavaScript(Writer out, String str) throws IOException {
escapeJavaStyleString(out, str, true);
}
/**
* <p>Worker method for the {@link #escapeJavaScript(String)} method.</p>
*
* @param str String to escape values in, may be null
* @param escapeSingleQuotes escapes single quotes if <code>true</code>
* @return the escaped string
*/
private static String escapeJavaStyleString(String str, boolean escapeSingleQuotes) {
if (str == null) {
return null;
}
try {
StringWriter writer = new StringWriter(str.length() * 2);
escapeJavaStyleString(writer, str, escapeSingleQuotes);
return writer.toString();
} catch (IOException ioe) {
// this should never ever happen while writing to a StringWriter
ioe.printStackTrace();
return null;
}
}
/**
* <p>Worker method for the {@link #escapeJavaScript(String)} method.</p>
*
* @param out write to receieve the escaped string
* @param str String to escape values in, may be null
* @param escapeSingleQuote escapes single quotes if <code>true</code>
* @throws IOException if an IOException occurs
*/
private static void escapeJavaStyleString(Writer out, String str, boolean escapeSingleQuote) throws IOException {
if (out == null) {
throw new IllegalArgumentException("The Writer must not be null");
}
if (str == null) {
return;
}
int sz;
sz = str.length();
for (int i = 0; i < sz; i++) {
char ch = str.charAt(i);
// handle unicode
if (ch > 0xfff) {
out.write("\\u" + hex(ch));
} else if (ch > 0xff) {
out.write("\\u0" + hex(ch));
} else if (ch > 0x7f) {
out.write("\\u00" + hex(ch));
} else if (ch < 32) {
switch (ch) {
case '\b':
out.write('\\');
out.write('b');
break;
case '\n':
out.write('\\');
out.write('n');
break;
case '\t':
out.write('\\');
out.write('t');
break;
case '\f':
out.write('\\');
out.write('f');
break;
case '\r':
out.write('\\');
out.write('r');
break;
default :
if (ch > 0xf) {
out.write("\\u00" + hex(ch));
} else {
out.write("\\u000" + hex(ch));
}
break;
}
} else {
switch (ch) {
case '\'':
if (escapeSingleQuote) {
out.write('\\');
}
out.write('\'');
break;
case '"':
out.write('\\');
out.write('"');
break;
case '\\':
out.write('\\');
out.write('\\');
break;
default :
out.write(ch);
break;
}
}
}
}
/**
* <p>Returns an upper case hexadecimal <code>String</code> for the given
* character.</p>
*
* @param ch The character to convert.
* @return An upper case hexadecimal <code>String</code>
*/
private static String hex(char ch) {
return Integer.toHexString(ch).toUpperCase();
}
}
......@@ -15,6 +15,7 @@ import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.session.LocalSession;
import org.xmpp.packet.Packet;
import javax.net.ssl.SSLSession;
import java.net.UnknownHostException;
/**
......@@ -87,6 +88,13 @@ public interface Connection {
*/
public String getHostName() throws UnknownHostException;
/**
* Returns the underlying {@link SSLSession} for the connection.
*
* @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
*/
public SSLSession getSSLSession();
/**
* Close this session including associated socket connection. The order of
* events for closing the session is:
......@@ -290,9 +298,10 @@ public interface Connection {
* @param clientMode boolean indicating if this entity is a client or a server.
* @param remoteServer server name of the remote server we are connecting to or <tt>null</tt>
* when not in client mode.
* @param authentication policy to use for authenticating the remote peer.
* @throws Exception if an error occured while securing the connection.
*/
void startTLS(boolean clientMode, String remoteServer) throws Exception;
void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception;
/**
* Adds the compression filter to the connection but only filter incoming traffic. Do not filter
......@@ -349,4 +358,32 @@ public interface Connection {
*/
disabled
}
/**
* Enumeration that specifies if clients should be authenticated (and how) while
* negotiating TLS.
*/
enum ClientAuth {
/**
* No authentication will be performed on the client. Client credentials will not
* be verified while negotiating TLS.
*/
disabled,
/**
* Clients will try to be authenticated. Unlike {@link #needed}, if the client
* chooses not to provide authentication information about itself, the TLS negotiations
* will stop and the connection will be dropped. This option is only useful for
* engines in the server mode.
*/
wanted,
/**
* Clients need to be authenticated. Unlike {@link #wanted}, if the client
* chooses not to provide authentication information about itself, the TLS negotiations
* will continue. This option is only useful for engines in the server mode.
*/
needed
}
}
......@@ -32,7 +32,7 @@ import java.util.concurrent.LinkedBlockingQueue;
*/
public class ClusterManager {
private static String CLUSTER_PROPERTY_NAME = "cache.clustering.enabled";
private static String CLUSTER_PROPERTY_NAME = "clustering.enabled";
private static Queue<ClusterEventListener> listeners = new ConcurrentLinkedQueue<ClusterEventListener>();
private static BlockingQueue<Event> events = new LinkedBlockingQueue<Event>();
......
......@@ -19,11 +19,10 @@ import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.handler.DefaultHandler;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.security.SslSocketConnector;
import org.mortbay.jetty.security.SslSelectChannelConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.webapp.WebAppContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
......@@ -315,11 +314,11 @@ public class AdminConsolePlugin implements Plugin {
}
}
private class JiveSslConnector extends SslSocketConnector {
private class JiveSslConnector extends SslSelectChannelConnector {
@Override
protected SSLServerSocketFactory createFactory() throws Exception {
return SSLConfig.getServerSocketFactory();
protected SSLContext createSSLContext() throws Exception {
return SSLConfig.getSSLContext();
}
}
}
\ No newline at end of file
......@@ -10,22 +10,23 @@
*/
package org.jivesoftware.openfire.filetransfer.proxy;
import org.jivesoftware.util.*;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.DefaultCache;
import org.jivesoftware.util.cache.CacheFactory;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.filetransfer.FileTransferManager;
import org.jivesoftware.openfire.filetransfer.FileTransferRejectedException;
import org.jivesoftware.openfire.stats.Statistic;
import org.jivesoftware.openfire.stats.StatisticsManager;
import org.jivesoftware.openfire.stats.i18nStatistic;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.cache.CacheFactory;
import org.xmpp.packet.JID;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.InetAddress;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
......@@ -359,5 +360,9 @@ public class ProxyConnectionManager {
public double sample() {
return (ProxyOutputStream.amountTransfered.getAndSet(0) / 1000);
}
public boolean isPartialSample() {
return true;
}
}
}
\ No newline at end of file
......@@ -11,26 +11,25 @@
package org.jivesoftware.openfire.http;
import org.mortbay.jetty.Server;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.util.*;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.ContextHandler;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.handler.DefaultHandler;
import org.mortbay.jetty.handler.ContextHandler;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.jetty.security.SslSocketConnector;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.jivesoftware.util.*;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.XMPPServer;
import javax.net.ssl.SSLServerSocketFactory;
import java.util.Map;
import java.util.List;
import org.mortbay.jetty.security.SslSelectChannelConnector;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.webapp.WebAppContext;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.io.File;
import java.util.List;
import java.util.Map;
/**
*
......@@ -138,7 +137,7 @@ public final class HttpBindManager {
"the hosted domain");
}
SslSocketConnector sslConnector = new JiveSslConnector();
JiveSslConnector sslConnector = new JiveSslConnector();
sslConnector.setHost(getBindInterface());
sslConnector.setPort(securePort);
......@@ -430,11 +429,11 @@ public final class HttpBindManager {
}
}
private class JiveSslConnector extends SslSocketConnector {
private class JiveSslConnector extends SslSelectChannelConnector {
@Override
protected SSLServerSocketFactory createFactory() throws Exception {
return SSLConfig.getServerSocketFactory();
protected SSLContext createSSLContext() throws Exception {
return SSLConfig.getSSLContext();
}
}
......
......@@ -234,8 +234,7 @@ public class HttpBindServlet extends HttpServlet {
respond(response, createEmptyBody(), request.getMethod());
}
else {
connection
.setContinuation(ContinuationSupport.getContinuation(request, connection));
connection.setContinuation(ContinuationSupport.getContinuation(request, connection));
request.setAttribute("request-session", connection.getSession());
request.setAttribute("request", connection.getRequestId());
try {
......
......@@ -183,7 +183,7 @@ public class HttpConnection {
return deliverable;
}
this.isDelivered = true;
throw new HttpBindTimeoutException("Request " + requestId + " exceded response time from " +
throw new HttpBindTimeoutException("Request " + requestId + " exceeded response time from " +
"server of " + session.getWait() + " seconds.");
}
}
......@@ -310,7 +310,7 @@ public class HttpSession extends LocalClientSession {
* @return the time in milliseconds since the epoch that this session was last active.
*/
public synchronized long getLastActivity() {
if (connectionQueue.size() <= 0) {
if (connectionQueue.isEmpty()) {
return lastActivity;
}
else {
......@@ -417,7 +417,7 @@ public class HttpSession extends LocalClientSession {
protected void sendPendingPackets() {
// access blocked only on send to prevent deadlocks
synchronized (packetsToSend) {
if (packetsToSend.size() <= 0) {
if (packetsToSend.isEmpty()) {
return;
}
......
/**
* $RCSfile: $
* $Revision: $
* $Date: $
*
* Copyright (C) 2007 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.openfire.muc.cluster;
import org.dom4j.Element;
import org.jivesoftware.openfire.muc.MUCRole;
import org.jivesoftware.openfire.muc.NotAllowedException;
import org.jivesoftware.openfire.muc.spi.LocalMUCRoom;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.Presence;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Task that requests the cluster node hosting a room occupant to change his
* role and/or affiliation. If the occupant was not found or the change is
* not allowed then a <tt>null</tt> value is returned. Otherwise the DOM
* object representing the new presence of the room occupant is returned.
*
* @author Gaston Dombiak
*/
public class UpdateOccupantRequest extends MUCRoomTask {
private Element answer;
private String nickname;
private int role;
private int affiliation;
public UpdateOccupantRequest() {
}
public UpdateOccupantRequest(LocalMUCRoom room, String nickname, MUCRole.Affiliation newAffiliation,
MUCRole.Role newRole) {
super(room);
this.nickname = nickname;
this.role = newRole.ordinal();
this.affiliation = newAffiliation.ordinal();
}
public String getNickname() {
return nickname;
}
public MUCRole.Role getRole() {
return MUCRole.Role.values()[role];
}
public MUCRole.Affiliation getAffiliation() {
return MUCRole.Affiliation.values()[affiliation];
}
public Object getResult() {
return answer;
}
public void run() {
try {
Presence presence = getRoom().updateOccupant(this);
if (presence != null) {
answer = presence.getElement();
}
} catch (NotAllowedException e) {
// Do nothing. A null return value means that the operation failed
}
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
ExternalizableUtil.getInstance().writeSafeUTF(out, nickname);
ExternalizableUtil.getInstance().writeInt(out, role);
ExternalizableUtil.getInstance().writeInt(out, affiliation);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
nickname = ExternalizableUtil.getInstance().readSafeUTF(in);
role = ExternalizableUtil.getInstance().readInt(in);
affiliation = ExternalizableUtil.getInstance().readInt(in);
}
}
\ No newline at end of file
......@@ -1099,6 +1099,7 @@ public class LocalMUCRoom implements MUCRoom {
// Collect all the updated presences of these roles
for (MUCRole role : roles) {
// Update the presence with the new affiliation and role
if (role.isLocal()) {
role.setAffiliation(newAffiliation);
role.setRole(newRole);
// Notify the othe cluster nodes to update the occupant
......@@ -1106,6 +1107,21 @@ public class LocalMUCRoom implements MUCRoom {
// Prepare a new presence to be sent to all the room occupants
presences.add(role.getPresence().createCopy());
}
else {
// Ask the cluster node hosting the occupant to make the changes. Note that if the change
// is not allowed a NotAllowedException will be thrown
Element element = (Element) CacheFactory.doSynchronousClusterTask(
new UpdateOccupantRequest(this, role.getNickname(), newAffiliation, newRole),
role.getNodeID().toByteArray());
if (element != null) {
// Prepare a new presence to be sent to all the room occupants
presences.add(new Presence(element, true));
}
else {
throw new NotAllowedException();
}
}
}
// Answer all the updated presences
return presences;
}
......@@ -1467,17 +1483,43 @@ public class LocalMUCRoom implements MUCRoom {
}
public void occupantUpdated(UpdateOccupant update) {
RemoteMUCRole occupantRole = (RemoteMUCRole) occupants.get(update.getNickname().toLowerCase());
MUCRole occupantRole = occupants.get(update.getNickname().toLowerCase());
if (occupantRole != null) {
if (!occupantRole.isLocal()) {
occupantRole.setPresence(update.getPresence());
try {
occupantRole.setRole(update.getRole());
occupantRole.setAffiliation(update.getAffiliation());
} catch (NotAllowedException e) {
// Ignore. Should never happen with remote roles
}
}
else {
Log.error("Tried to update local occupant with info of local occupant?. Occupant nickname: " +
update.getNickname());
}
}
else {
Log.debug("Failed to update information of room occupant. Occupant nickname: " + update.getNickname());
}
}
public Presence updateOccupant(UpdateOccupantRequest updateRequest) throws NotAllowedException {
MUCRole occupantRole = occupants.get(updateRequest.getNickname().toLowerCase());
if (occupantRole != null) {
occupantRole.setAffiliation(updateRequest.getAffiliation());
occupantRole.setRole(updateRequest.getRole());
// Notify the othe cluster nodes to update the occupant
CacheFactory.doClusterTask(new UpdateOccupant(this, occupantRole));
return occupantRole.getPresence();
}
else {
Log.debug("Failed to update information of local room occupant. Occupant nickname: " +
updateRequest.getNickname());
}
return null;
}
public void nicknameChanged(MUCRole occupantRole, Presence newPresence, String oldNick, String newNick) {
// Ask other cluster nodes to update the nickname of the occupant
ChangeNickname request = new ChangeNickname(this, oldNick, newNick, newPresence.createCopy());
......
......@@ -1388,6 +1388,10 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
public double sample() {
return getNumberChatRooms();
}
public boolean isPartialSample() {
return false;
}
};
StatisticsManager.getInstance().addStatistic(roomsStatKey, statistic);
}
......@@ -1414,6 +1418,10 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
public double sample() {
return getNumberRoomOccupants();
}
public boolean isPartialSample() {
return false;
}
};
StatisticsManager.getInstance().addStatistic(occupantsStatKey, statistic);
}
......@@ -1440,6 +1448,10 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
public double sample() {
return getNumberConnectedUsers(false);
}
public boolean isPartialSample() {
return false;
}
};
StatisticsManager.getInstance().addStatistic(usersStatKey, statistic);
}
......@@ -1464,9 +1476,13 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
}
public double sample() {
// TODO Get these value from the other cluster nodes
return inMessages.getAndSet(0);
}
public boolean isPartialSample() {
// Get this value from the other cluster nodes
return true;
}
};
StatisticsManager.getInstance().addMultiStatistic(incomingStatKey, trafficStatGroup, statistic);
}
......@@ -1493,6 +1509,11 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
public double sample() {
return outMessages.getAndSet(0);
}
public boolean isPartialSample() {
// Each cluster node knows the total across the cluster
return false;
}
};
StatisticsManager.getInstance().addMultiStatistic(outgoingStatKey, trafficStatGroup, statistic);
}
......
......@@ -22,8 +22,7 @@ import org.xmpp.packet.IQ;
import org.xmpp.packet.Message;
import org.xmpp.packet.Presence;
/**
* Handler of XML stanzas sent by clients connected directly to the server. Received packet will
/** Handler of XML stanzas sent by clients connected directly to the server. Received packet will
* have their FROM attribute overriden to avoid spoofing.<p>
*
* By default the hostname specified in the stream header sent by clients will not be validated.
......@@ -90,4 +89,17 @@ public class ClientStanzaHandler extends StanzaHandler {
packet.setFrom(session.getAddress());
super.processMessage(packet);
}
void startTLS() throws Exception {
Connection.ClientAuth policy;
try {
policy =
Connection.ClientAuth.valueOf(JiveGlobals.getProperty("xmpp.client.cert.policy",
"required"));
} catch (IllegalArgumentException e) {
policy = Connection.ClientAuth.disabled;
}
connection.startTLS(false, null, policy);
}
}
......@@ -131,4 +131,8 @@ public class MultiplexerStanzaHandler extends StanzaHandler {
}
return false;
}
void startTLS() throws Exception {
connection.startTLS(false, null, Connection.ClientAuth.disabled);
}
}
......@@ -19,8 +19,8 @@ import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.AuthorizationManager;
import org.jivesoftware.openfire.auth.AuthFactory;
import org.jivesoftware.openfire.auth.AuthToken;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.session.*;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
......@@ -117,6 +117,8 @@ public class SASLAuthentication {
* the session's connection is not secured then only include the SASL mechanisms that don't
* require TLS.
*
* @param session The current session
*
* @return a string with the valid SASL mechanisms available for the specified session.
*/
public static String getSASLMechanisms(Session session) {
......@@ -218,6 +220,12 @@ public class SASLAuthentication {
token = new byte[0];
}
}
if (mechanism.equals("DIGEST-MD5")) {
// RFC2831 (DIGEST-MD5) says the client MAY provide an initial response on subsequent
// authentication. Java SASL does not (currently) support this and thows an exception
// if we try. This violates the RFC, so we just strip any initial token.
token = new byte[0];
}
byte[] challenge = ss.evaluateResponse(token);
if (ss.isComplete()) {
authenticationSuccessful(session, ss.getAuthorizationID(),
......@@ -435,14 +443,20 @@ public class SASLAuthentication {
Log.warn("Error retrieving client certificates of: " + session, e);
}
}
else {
else if (session instanceof LocalClientSession) {
// Client EXTERNALL login
Log.debug("SASLAuthentication: EXTERNAL authentication via SSL certs for c2s connection");
// This may be null, we will deal with that later
String username = doc.getTextTrim();
String username = new String(StringUtils.decodeBase64(doc.getTextTrim()), CHARSET);
String principal = "";
ArrayList<String> principals = new ArrayList<String>();
SocketConnection connection = (SocketConnection)session.getConnection();
Connection connection = session.getConnection();
if (connection.getSSLSession() == null) {
Log.debug("SASLAuthentication: EXTERNAL authentication requested, but no SSL/TLS connection found.");
authenticationFailed(session);
return Status.failed;
}
try {
for (Certificate certificate : connection.getSSLSession().getPeerCertificates()) {
principals.addAll(CertificateManager.getPeerIdentities((X509Certificate)certificate));
......@@ -452,6 +466,9 @@ public class SASLAuthentication {
Log.warn("Error retrieving client certificates of: " + session, e);
}
principal = principals.get(0);
if (username == null || username.length() == 0) {
// No username was provided, according to XEP-0178 we need to:
// * attempt to get it from the cert first
......@@ -461,8 +478,6 @@ public class SASLAuthentication {
// We set principal to the first one in the list to have a sane default
// If this list is empty, then the cert had no identity at all, which
// will cause an authorization failure
principal = principals.get(0);
for(String princ : principals) {
String u = AuthorizationManager.map(princ);
if(!u.equals(princ)) {
......@@ -471,14 +486,21 @@ public class SASLAuthentication {
break;
}
}
if (username == null || username.length() == 0) {
// Still no username. Punt.
username = principal;
}
Log.debug("SASLAuthentication: no username requested, using "+username);
}
//Its possible that either/both username and principal are null here
//The providers should not allow a null authorization
if (AuthorizationManager.authorize(username,principal)) {
Log.debug("SASLAuthentication: "+principal+" authorized to "+username);
return Status.authenticated;
}
} else {
Log.debug("SASLAuthentication: unknown session type. Cannot perform EXTERNAL authentication");
}
authenticationFailed(session);
return Status.failed;
......
......@@ -16,6 +16,8 @@ import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import javax.net.ssl.*;
import javax.net.ServerSocketFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
......@@ -33,7 +35,7 @@ import java.util.List;
*/
public class SSLConfig {
private static SSLJiveServerSocketFactory sslFactory;
private static SSLServerSocketFactory sslFactory;
private static KeyStore keyStore;
private static String keypass;
private static KeyStore trustStore;
......@@ -41,6 +43,7 @@ public class SSLConfig {
private static String keyStoreLocation;
private static String trustStoreLocation;
private static String storeType;
private static SSLContext sslContext;
private SSLConfig() {
}
......@@ -73,9 +76,7 @@ public class SSLConfig {
trustStore = KeyStore.getInstance(storeType);
trustStore.load(new FileInputStream(trustStoreLocation), trustpass.toCharArray());
sslFactory = (SSLJiveServerSocketFactory)SSLJiveServerSocketFactory.getInstance(
algorithm, keyStore, trustStore);
resetFactory();
}
catch (Exception e) {
Log.error("SSLConfig startup problem.\n" +
......@@ -88,37 +89,52 @@ public class SSLConfig {
trustStore = null;
sslFactory = null;
}
// Reset ssl factoty when certificates are modified
CertificateManager.addListener(new CertificateEventListener() {
public void certificateCreated(KeyStore keyStore, String alias, X509Certificate cert) {
// Reset ssl factory since keystores have changed
resetFactory(keyStore);
public void certificateCreated(KeyStore keyStore, String alias, X509Certificate cert) {
resetFactory();
}
public void certificateDeleted(KeyStore keyStore, String alias) {
// Reset ssl factory since keystores have changed
resetFactory(keyStore);
resetFactory();
}
public void certificateSigned(KeyStore keyStore, String alias,
List<X509Certificate> certificates) {
// Reset ssl factory since keystores have changed
resetFactory(keyStore);
public void certificateSigned(KeyStore keyStore, String alias, List<X509Certificate> certificates) {
resetFactory();
}
});
}
private void resetFactory(KeyStore keyStore) {
private static void resetFactory() {
try {
String algorithm = JiveGlobals.getProperty("xmpp.socket.ssl.algorithm", "TLS");
sslFactory = (SSLJiveServerSocketFactory)SSLJiveServerSocketFactory.getInstance(
algorithm, keyStore, trustStore);
sslContext = SSLContext.getInstance(algorithm);
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, SSLConfig.getKeyPassword().toCharArray());
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(trustStore);
sslContext.init(keyFactory.getKeyManagers(),
trustFactory.getTrustManagers(),
new java.security.SecureRandom());
sslFactory = sslContext.getServerSocketFactory();
}
catch (IOException e) {
Log.error("Error while resetting ssl factory", e);
catch (Exception e) {
Log.error("SSLConfig factory setup problem.\n" +
" storeType: [" + storeType + "]\n" +
" keyStoreLocation: [" + keyStoreLocation + "]\n" +
" keypass: [" + keypass + "]\n" +
" trustStoreLocation: [" + trustStoreLocation+ "]\n" +
" trustpass: [" + trustpass + "]", e);
keyStore = null;
trustStore = null;
sslFactory = null;
}
}
});
}
public static String getKeyPassword() {
return keypass;
......@@ -199,7 +215,11 @@ public class SSLConfig {
return storeType;
}
public static SSLJiveServerSocketFactory getServerSocketFactory() {
public static SSLContext getSSLContext() {
return sslContext;
}
public static SSLServerSocketFactory getServerSocketFactory() {
return sslFactory;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* 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.openfire.net;
import org.jivesoftware.util.Log;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.KeyStore;
/**
* Securue socket factory wrapper allowing simple setup of all security
* SSL related parameters.
*
* @author Iain Shigeoka
*/
public class SSLJiveServerSocketFactory extends SSLServerSocketFactory {
public static SSLServerSocketFactory getInstance(String algorithm,
KeyStore keystore,
KeyStore truststore) throws
IOException {
try {
SSLContext sslcontext = SSLContext.getInstance(algorithm);
SSLServerSocketFactory factory;
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keystore, SSLConfig.getKeyPassword().toCharArray());
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(truststore);
sslcontext.init(keyFactory.getKeyManagers(),
trustFactory.getTrustManagers(),
new java.security.SecureRandom());
factory = sslcontext.getServerSocketFactory();
return new SSLJiveServerSocketFactory(factory);
}
catch (Exception e) {
Log.error(e);
throw new IOException(e.getMessage());
}
}
private SSLServerSocketFactory factory;
private SSLJiveServerSocketFactory(SSLServerSocketFactory factory) {
this.factory = factory;
}
public ServerSocket createServerSocket(int i) throws IOException {
return factory.createServerSocket(i);
}
public ServerSocket createServerSocket(int i, int i1) throws IOException {
return factory.createServerSocket(i, i1);
}
public ServerSocket createServerSocket(int i, int i1, InetAddress inetAddress) throws IOException {
return factory.createServerSocket(i, i1, inetAddress);
}
public String[] getDefaultCipherSuites() {
return factory.getDefaultCipherSuites();
}
public String[] getSupportedCipherSuites() {
return factory.getSupportedCipherSuites();
}
}
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2007 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.openfire.net;
import org.dom4j.Element;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.PacketRouter;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.session.LocalIncomingServerSession;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.JiveGlobals;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmpp.packet.*;
/**
* Handler of XML stanzas sent by remote servers. Remote servers that send stanzas
* with no TO or FROM will get their connections closed. Moreover, remote servers
* that try to send stanzas from a not validated domain will also get their connections
* closed.<p>
*
* Server-to-server communication requires two TCP connections between the servers where
* one is used for sending packets whilst the other connection is used for receiving packets.
* The connection used for receiving packets will use a ServerStanzaHandler since the other
* connection will not receive packets.<p>
*
* TODO Finish migration of s2s to use NIO instead of blocking threads. Migrate from ServerSocketReader.
*
* @author Gaston Dombiak
*/
public class ServerStanzaHandler extends StanzaHandler {
public ServerStanzaHandler(PacketRouter router, String serverName, Connection connection) {
super(router, serverName, connection);
}
boolean processUnknowPacket(Element doc) throws UnauthorizedException {
// Handle subsequent db:result packets
if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) {
if (!((LocalIncomingServerSession) session).validateSubsequentDomain(doc)) {
throw new UnauthorizedException("Failed to validate domain when using piggyback.");
}
return true;
}
else if ("db".equals(doc.getNamespacePrefix()) && "verify".equals(doc.getName())) {
// The Receiving Server is reusing an existing connection for sending the
// Authoritative Server a request for verification of a key
((LocalIncomingServerSession) session).verifyReceivedKey(doc);
return true;
}
return false;
}
String getNamespace() {
return "jabber:server";
}
boolean validateHost() {
return true;
}
boolean validateJIDs() {
// TODO Should we trust other servers???
return false;
}
boolean createSession(String namespace, String serverName, XmlPullParser xpp, Connection connection)
throws XmlPullParserException {
// TODO Finish implementation
/*if ("jabber:server".equals(namespace)) {
// The connected client is a server so create an IncomingServerSession
session = LocalIncomingServerSession.createSession(serverName, reader, connection);
return true;
}*/
return false;
}
void startTLS() throws Exception {
// TODO Finish implementation. We need to get the name of the remote server!?!?
boolean needed = JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify", true) &&
JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.chain", true) &&
!JiveGlobals.getBooleanProperty("xmpp.server.certificate.accept-selfsigned", false);
connection.startTLS(true, "IMPLEMENT_ME", needed ? Connection.ClientAuth.needed : Connection.ClientAuth.wanted);
}
protected void processIQ(IQ packet) throws UnauthorizedException {
packetReceived(packet);
// Actually process the packet
super.processIQ(packet);
}
protected void processPresence(Presence packet) throws UnauthorizedException {
packetReceived(packet);
// Actually process the packet
super.processPresence(packet);
}
protected void processMessage(Message packet) throws UnauthorizedException {
packetReceived(packet);
// Actually process the packet
super.processMessage(packet);
}
/**
* Make sure that the received packet has a TO and FROM values defined and that it was sent
* from a previously validated domain. If the packet does not matches any of the above
* conditions then a PacketRejectedException will be thrown.
*
* @param packet the received packet.
* @throws UnauthorizedException if the packet does not include a TO or FROM or if the packet
* was sent from a domain that was not previously validated.
*/
private void packetReceived(Packet packet) throws UnauthorizedException {
if (packet.getTo() == null || packet.getFrom() == null) {
Log.debug("Closing IncomingServerSession due to packet with no TO or FROM: " +
packet.toXML());
// Send a stream error saying that the packet includes no TO or FROM
StreamError error = new StreamError(StreamError.Condition.improper_addressing);
connection.deliverRawText(error.toXML());
throw new UnauthorizedException("Packet with no TO or FROM attributes");
}
else if (!((LocalIncomingServerSession) session).isValidDomain(packet.getFrom().getDomain())) {
Log.debug("Closing IncomingServerSession due to packet with invalid domain: " +
packet.toXML());
// Send a stream error saying that the packet includes an invalid FROM
StreamError error = new StreamError(StreamError.Condition.invalid_from);
connection.deliverRawText(error.toXML());
throw new UnauthorizedException("Packet with no TO or FROM attributes");
}
}
}
......@@ -136,6 +136,10 @@ public class ServerTrafficCounter {
// Divide result by 1024 so that we return the result in Kb.
return incomingCounter.getAndSet(0)/1024;
}
public boolean isPartialSample() {
return true;
}
};
StatisticsManager.getInstance()
.addMultiStatistic(incomingStatKey, trafficStatGroup, statistic);
......@@ -163,6 +167,10 @@ public class ServerTrafficCounter {
public double sample() {
return outgoingCounter.getAndSet(0)/1024;
}
public boolean isPartialSample() {
return true;
}
};
StatisticsManager.getInstance()
.addMultiStatistic(outgoingStatKey, trafficStatGroup, statistic);
......
......@@ -152,7 +152,7 @@ public class SocketConnection implements Connection {
return tlsStreamHandler;
}
public void startTLS(boolean clientMode, String remoteServer) throws IOException {
public void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws IOException {
if (!secure) {
secure = true;
// Prepare for TLS
......
......@@ -69,7 +69,7 @@ abstract class SocketReadingMode {
}
// Client requested to secure the connection using TLS. Negotiate TLS.
try {
socketReader.connection.startTLS(false, null);
socketReader.connection.startTLS(false, null, Connection.ClientAuth.disabled);
}
catch (IOException e) {
Log.error("Error while negotiating TLS: " + socketReader.connection, e);
......
......@@ -43,7 +43,7 @@ public abstract class StanzaHandler {
* The utf-8 charset for decoding and encoding Jabber packet streams.
*/
protected static String CHARSET = "UTF-8";
private Connection connection;
protected Connection connection;
// DANIELE: Indicate if a session is already created
private boolean sessionCreated = false;
......@@ -347,8 +347,9 @@ public abstract class StanzaHandler {
*
* @param doc the DOM element of an unkown type.
* @return true if a received packet has been processed.
* @throws UnauthorizedException if stanza failed to be processed. Connection will be closed.
*/
abstract boolean processUnknowPacket(Element doc);
abstract boolean processUnknowPacket(Element doc) throws UnauthorizedException;
/**
* Tries to secure the connection using TLS. If the connection is secured then reset
......@@ -372,7 +373,7 @@ public abstract class StanzaHandler {
}
// Client requested to secure the connection using TLS. Negotiate TLS.
try {
connection.startTLS(false, null);
startTLS();
}
catch (Exception e) {
Log.error("Error while negotiating TLS", e);
......@@ -383,6 +384,8 @@ public abstract class StanzaHandler {
return true;
}
abstract void startTLS() throws Exception;
/**
* TLS negotiation was successful so open a new stream and offer the new stream features.
* The new stream features will include available SASL mechanisms and specific features
......
......@@ -19,6 +19,7 @@ import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import javax.net.ssl.SSLSession;
import java.util.HashMap;
import java.util.Map;
......@@ -56,6 +57,11 @@ public abstract class VirtualConnection implements Connection {
return 0;
}
public SSLSession getSSLSession() {
// Ignore
return null;
}
public boolean isClosed() {
if (session == null) {
return closed;
......@@ -108,7 +114,7 @@ public abstract class VirtualConnection implements Connection {
return null;
}
public void startTLS(boolean clientMode, String remoteServer) throws Exception {
public void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception {
//Ignore
}
......
......@@ -34,6 +34,7 @@ import org.xmpp.packet.Packet;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.SSLSession;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
......@@ -134,6 +135,10 @@ public class NIOConnection implements Connection {
return ((InetSocketAddress) ioSession.getRemoteAddress()).getAddress().getHostName();
}
public SSLSession getSSLSession() {
return (SSLSession) ioSession.getAttribute(SSLFilter.SSL_SESSION);
}
public PacketDeliverer getPacketDeliverer() {
return backupDeliverer;
}
......@@ -272,7 +277,7 @@ public class NIOConnection implements Connection {
}
}
public void startTLS(boolean clientMode, String remoteServer) throws Exception {
public void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception {
KeyStore ksKeys = SSLConfig.getKeyStore();
String keypass = SSLConfig.getKeyPassword();
......@@ -284,9 +289,7 @@ public class NIOConnection implements Connection {
// TrustManager's decide whether to allow connections.
TrustManager[] tm = SSLJiveTrustManagerFactory.getTrustManagers(ksTrust, trustpass);
// TODO Set proper value when s2s is supported
boolean needClientAuth = false;
if (clientMode || needClientAuth) {
if (clientMode || authentication == ClientAuth.needed || authentication == ClientAuth.wanted) {
// Check if we can trust certificates presented by the server
tm = new TrustManager[]{new ServerTrustManager(remoteServer, ksTrust)};
}
......@@ -297,22 +300,15 @@ public class NIOConnection implements Connection {
SSLFilter filter = new SSLFilter(tlsContext);
filter.setUseClientMode(clientMode);
if (needClientAuth) {
// Only REQUIRE client authentication if we are fully verifying certificates
if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify", true) &&
JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.chain", true) &&
!JiveGlobals
.getBooleanProperty("xmpp.server.certificate.accept-selfsigned", false))
{
if (authentication == ClientAuth.needed) {
filter.setNeedClientAuth(true);
}
else {
else if (authentication == ClientAuth.wanted) {
// Just indicate that we would like to authenticate the client but if client
// certificates are self-signed or have no certificate chain then we are still
// good
filter.setWantClientAuth(true);
}
}
// TODO Temporary workaround (placing SSLFilter before ExecutorFilter) to avoid deadlock. Waiting for
// MINA devs feedback
ioSession.getFilterChain().addBefore("org.apache.mina.common.ExecutorThreadModel", "tls", filter);
......
......@@ -363,7 +363,10 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
Element proceed = reader.parseDocument().getRootElement();
if (proceed != null && proceed.getName().equals("proceed")) {
Log.debug("OS - Negotiating TLS with " + hostname);
connection.startTLS(true, hostname);
boolean needed = JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify", true) &&
JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.chain", true) &&
!JiveGlobals.getBooleanProperty("xmpp.server.certificate.accept-selfsigned", false);
connection.startTLS(true, hostname, needed ? Connection.ClientAuth.needed : Connection.ClientAuth.wanted);
Log.debug("OS - TLS negotiation with " + hostname + " was successful");
// TLS negotiation was successful so initiate a new stream
......
......@@ -402,7 +402,14 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
trustFactory.getTrustManagers(),
new java.security.SecureRandom());
sslSocketAcceptor.getFilterChain().addFirst("tls", new SSLFilter(sslContext));
SSLFilter sslFilter = new SSLFilter(sslContext);
if (JiveGlobals.getProperty("xmpp.client.cert.policy","disabled").equals("needed")) {
sslFilter.setNeedClientAuth(true);
}
else if(JiveGlobals.getProperty("xmpp.client.cert.policy","disabled").equals("wanted")) {
sslFilter.setWantClientAuth(true);
}
sslSocketAcceptor.getFilterChain().addFirst("tls", sslFilter);
}
catch (Exception e) {
......
......@@ -32,6 +32,7 @@ import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.lock.LockManager;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
import org.xmpp.packet.JID;
......@@ -44,6 +45,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
/**
* Simple in memory implementation of the PresenceManager interface.
......@@ -501,7 +503,10 @@ public class PresenceManagerImpl extends BasicModule implements PresenceManager
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Lock lock = LockManager.getLock(username + "pr");
try {
lock.lock();
if (!offlinePresenceCache.containsKey(username) || !lastActivityCache.containsKey(username)) {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_OFFLINE_PRESENCE);
pstmt.setString(1, username);
......@@ -520,11 +525,13 @@ public class PresenceManagerImpl extends BasicModule implements PresenceManager
lastActivityCache.put(username, NULL_LONG);
}
}
}
catch (SQLException sqle) {
Log.error(sqle);
}
finally {
DbConnectionManager.closeConnection(rs, pstmt, con);
lock.unlock();
}
}
}
\ No newline at end of file
......@@ -53,6 +53,24 @@ public interface Statistic {
*/
public double sample();
/**
* Returns true if the sample value represents only the value of the cluster node
* or otherwise it represents the value of the entire cluster. Statistics that keep
* only a sample of the local node will need to get added up with the value of the
* other cluster nodes. On the other hand, statistics that hold the value of the
* entire cluster can be sampled in any cluster node and they don't need to get
* added up.<p>
*
* An example of a partial sample statistic would be network traffic. Each node keeps
* track of its own network traffic information. Whilst an example of a total sample
* statistic would be user sessions since every cluster node knows the total number of
* connected users across the cluster.
*
* @return true if the sample value represents only the value of the cluster node
* or otherwise it represents the value of the entire cluster.
*/
public boolean isPartialSample();
/**
* The type of statistic.
*/
......
......@@ -17,6 +17,7 @@ import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.apache.commons.lang.StringEscapeUtils;
import java.io.*;
import java.util.*;
......@@ -327,7 +328,7 @@ public class XMLProperties {
childElement.addCDATA(value.substring(9, value.length()-3));
}
else {
childElement.setText(value);
childElement.setText(StringEscapeUtils.escapeXml(value));
}
}
saveProperties();
......@@ -379,6 +380,9 @@ public class XMLProperties {
* @param value the new value for the property.
*/
public synchronized void setProperty(String name, String value) {
if(!StringEscapeUtils.escapeXml(name).equals(name)) {
throw new IllegalArgumentException("Property name cannot contain XML entities.");
}
if (name == null) {
return;
}
......@@ -413,7 +417,7 @@ public class XMLProperties {
element.addCDATA(value.substring(9, value.length()-3));
}
else {
element.setText(value);
element.setText(StringEscapeUtils.escapeXml(value));
}
// Write the XML properties to disk
saveProperties();
......@@ -456,6 +460,8 @@ public class XMLProperties {
/**
* Builds the document XML model up based the given reader of XML data.
* @param in the input stream used to build the xml document
* @throws java.io.IOException thrown when an error occurs reading the input stream.
*/
private void buildDoc(Reader in) throws IOException {
try {
......
......@@ -84,12 +84,14 @@ public class BroadcastPlugin implements Plugin, Component, PropertyEventListener
public void destroyPlugin() {
PropertyEventDispatcher.removeListener(this);
// Unregister component.
if (componentManager != null) {
try {
componentManager.removeComponent(serviceName);
}
catch (Exception e) {
componentManager.getLog().error(e);
}
}
componentManager = null;
pluginManager = null;
sessionManager = null;
......
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Broadcast Plugin Changelog</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY, TD, TH {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
}
H2 {
font-size : 10pt;
font-weight : bold;
padding-left : 1em;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new;
font-weight : bold;
color : #060;
}
PRE {
font-family : courier new;
font-size : 100%;
}
</style>
</head>
<body>
<h1>
XML Debugger Plugin Changelog
</h1>
<p><b>1.0.0</b> -- September 5, 2007</p>
<ul>
<li>Initial release.</li>
</ul>
</body>
</html>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
Plugin configuration for the sessions debugger plugin.
-->
<plugin>
<class>org.jivesoftware.openfire.plugin.DebuggerPlugin</class>
<name>Debugger Plugin</name>
<description>Prints XML traffic to the stdout (raw and interpreted XML)</description>
<author>Jive Software</author>
<version>1.0</version>
<minServerVersion>3.2.0</minServerVersion>
</plugin>
\ No newline at end of file
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>XML Debugger Plugin Readme</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY, TD, TH {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
}
H2 {
font-size : 10pt;
font-weight : bold;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new;
font-weight : bold;
color : #060;
}
PRE {
font-family : courier new;
font-size : 100%;
}
</style>
</head>
<body>
<h1>
XML Debugger Plugin Readme
</h1>
<h2>Overview</h2>
<p>
The xml debugger plugin prints XML traffic to the stdout. The plugin will print raw XML as it
was received and sent by the server as well as interpreted XML (i.e. parsed XML). By default the
plugin will only print raw XML.
</p>
<h2>Installation</h2>
<p>Copy xmldebugger.jar into the plugins directory of your Openfire installation. The
plugin will then be automatically deployed. To upgrade to a new version, copy the new
xmldebugger.jar file over the existing file.</p>
<h2>Configuration</h2>
The debugger plugin is configured via Openfire system properties. These can
be configured under Server/Server Manager/System Properties:
<ul>
<li><tt>plugin.debugger.interpretedAllowed</tt> -- true to print XML packets
after they were parsed by the server. This only includes incoming traffic. The
default value is false.</li>
</ul>
<h2>Using the Plugin</h2>
Traffic generated by sessions created after the plugin was installed will be captured and
printed.
</body>
</html>
/**
* $RCSfile: $
* $Revision: $
* $Date: $
*
* Copyright (C) 2007 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.openfire.plugin;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.PropertyEventDispatcher;
import org.jivesoftware.util.PropertyEventListener;
import java.io.File;
import java.util.Map;
/**
* Debugger plugin that prints XML traffic to stdout. By default it will only print
* raw XML traffic (by using a MINA filter). To turn on printing of interpreted XML
* (i.e. parsed XML) just enable the system property <tt>plugin.debugger.interpretedAllowed</tt>.
* There is no need to restart the plugin or the server.
*
* @author Gaston Dombiak
*/
public class DebuggerPlugin implements Plugin, PropertyEventListener {
private RawPrintFilter defaultPortFilter;
private RawPrintFilter oldPortFilter;
private InterpretedXMLPrinter interpretedPrinter;
public void initializePlugin(PluginManager manager, File pluginDirectory) {
// Add filter to filter chain builder
ConnectionManagerImpl connManager = (ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager();
defaultPortFilter = new RawPrintFilter();
connManager.getSocketAcceptor().getFilterChain().addBefore("xmpp", "rawDebugger", defaultPortFilter);
oldPortFilter = new RawPrintFilter();
connManager.getSSLSocketAcceptor().getFilterChain().addBefore("xmpp", "rawDebugger", oldPortFilter);
interpretedPrinter = new InterpretedXMLPrinter();
if (JiveGlobals.getBooleanProperty("plugin.debugger.interpretedAllowed")) {
// Add the packet interceptor that prints interpreted XML
InterceptorManager.getInstance().addInterceptor(interpretedPrinter);
}
// Listen to property events
PropertyEventDispatcher.addListener(this);
}
public void destroyPlugin() {
// Stop listening to property events
PropertyEventDispatcher.removeListener(this);
// Remove filter from filter chain builder
ConnectionManagerImpl connManager = (ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager();
if (connManager.getSocketAcceptor().getFilterChain().contains("rawDebugger")) {
connManager.getSocketAcceptor().getFilterChain().remove("rawDebugger");
}
if (connManager.getSSLSocketAcceptor().getFilterChain().contains("rawDebugger")) {
connManager.getSSLSocketAcceptor().getFilterChain().remove("rawDebugger");
}
// Remove the filters from existing sessions
if (defaultPortFilter != null) {
defaultPortFilter.shutdown();
}
if (oldPortFilter != null) {
oldPortFilter.shutdown();
}
// Remove the packet interceptor that prints interpreted XML
InterceptorManager.getInstance().removeInterceptor(interpretedPrinter);
defaultPortFilter = null;
oldPortFilter = null;
interpretedPrinter = null;
}
public void propertySet(String property, Map<String, Object> params) {
if (property.equals("plugin.debugger.interpretedAllowed")) {
if (Boolean.parseBoolean((String)params.get("value"))) {
InterceptorManager.getInstance().addInterceptor(interpretedPrinter);
}
else {
InterceptorManager.getInstance().removeInterceptor(interpretedPrinter);
}
}
}
public void propertyDeleted(String property, Map<String, Object> params) {
if (property.equals("plugin.debugger.interpretedAllowed")) {
InterceptorManager.getInstance().removeInterceptor(interpretedPrinter);
}
}
public void xmlPropertySet(String property, Map<String, Object> params) {
// Do nothing
}
public void xmlPropertyDeleted(String property, Map<String, Object> params) {
// Do nothing
}
}
/**
* $RCSfile: $
* $Revision: $
* $Date: $
*
* Copyright (C) 2007 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.openfire.plugin;
import org.jivesoftware.openfire.interceptor.PacketInterceptor;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.session.Session;
import org.xmpp.packet.Packet;
/**
* Packet interceptor that prints to the stdout XML packets (i.e. XML after
* it was parsed).<p>
*
* If you find in the logs an entry for raw XML, an entry that a session was closed and
* never find the corresponding interpreted XML for the raw XML then there was an error
* while parsing the XML that closed the session.
*
* @author Gaston Dombiak.
*/
public class InterpretedXMLPrinter implements PacketInterceptor {
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed)
throws PacketRejectedException {
if (!processed && incoming) {
System.out.println("INTERPRETED: " + packet.toXML());
}
}
}
/**
* $RCSfile: $
* $Revision: $
* $Date: $
*
* Copyright (C) 2007 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.openfire.plugin;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoFilterAdapter;
import org.apache.mina.common.IoSession;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* MINA filter that prints to the stdout received XML stanzas before they are actually parsed and
* also prints XML stanzas as sent to the XMPP entities. Moreover, it also prints information when
* a session is closed.
*
* @author Gaston Dombiak
*/
public class RawPrintFilter extends IoFilterAdapter {
private Collection<IoSession> sessions = new ConcurrentLinkedQueue<IoSession>();
public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
// Decode the bytebuffer and print it to the stdout
if (message instanceof ByteBuffer) {
ByteBuffer byteBuffer = (ByteBuffer) message;
// Keep current position in the buffer
int currentPos = byteBuffer.position();
// Decode buffer
Charset encoder = Charset.forName("UTF-8");
CharBuffer charBuffer = encoder.decode(byteBuffer.buf());
// Print buffer content
System.out.println("RECV (" + session.hashCode() + "): " + charBuffer);
// Reset to old position in the buffer
byteBuffer.position(currentPos);
}
// Pass the message to the next filter
super.messageReceived(nextFilter, session, message);
}
public void messageSent(NextFilter nextFilter, IoSession session, Object message) throws Exception {
System.out.println("SENT (" + session.hashCode() + "): " +
Charset.forName("UTF-8").decode(((ByteBuffer) message).buf()));
// Pass the message to the next filter
super.messageSent(nextFilter, session, message);
}
public void shutdown() {
// Remove this filter from sessions that are using it
for (IoSession session : sessions) {
session.getFilterChain().remove("rawDebugger");
}
sessions = null;
}
public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
// Keep track of sessions using this filter
sessions.add(session);
super.sessionCreated(nextFilter, session);
}
public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
// Update list of sessions using this filter
sessions.remove(session);
// Print that a session was closed
System.out.println("CLOSED (" + session.hashCode() + ") ");
super.sessionClosed(nextFilter, session);
}
}
\ No newline at end of file
......@@ -13,7 +13,6 @@ package org.jivesoftware.util;
import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.util.Iterator;
public class XMLPropertiesTest extends TestCase {
......@@ -49,4 +48,10 @@ public class XMLPropertiesTest extends TestCase {
i++;
}
}
public void testGetPropertyWithXMLEntity() throws Exception {
String xml = "<root><foo>foo&amp;bar</foo></root>";
XMLProperties props = new XMLProperties(new ByteArrayInputStream(xml.getBytes()));
assertEquals("foo&bar", props.getProperty("foo"));
}
}
......@@ -167,11 +167,13 @@
<input type="submit" value="<fmt:message key="user.roster.edit" />">
</form>
<% if (sharedGroups.isEmpty()) { %>
<form style="display: inline" action="user-roster-delete.jsp">
<input type="hidden" name="jid" value="<%= jid %>">
<input type="hidden" name="username" value="<%= username %>">
<input type="submit" value="<fmt:message key="global.delete" />">
</form>
<% } %>
</body>
</html>
\ No newline at end of file
......@@ -268,7 +268,9 @@
if (count != 0) {
out.print(", ");
}
out.print("<u>"+group.getName()+"</u>");
out.print("<a style='text-decoration: underline' href='group-edit.jsp?group="+URLEncoder.encode(group.getName(), "UTF-8")+"'>");
out.print(group.getName());
out.print("</a>");
count++;
}
}
......@@ -293,7 +295,7 @@
title="<fmt:message key="global.click_delete" />"
><img src="images/delete-16x16.gif" width="16" height="16" border="0" alt="<fmt:message key="global.click_delete" />"></a>
<% } else { %>
<img onclick='alert("<fmt:message key="user.roster.cant_delete" />")' src="images/forbidden-16x16.gif" width="16" height="16" border="0" alt="">
<img onclick='alert("<fmt:message key="user.roster.cant_delete" />")' src="images/lock.gif" width="16" height="16" border="0" alt="">
<% } %>
</td>
</tr>
......
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