Commit 6d17d5aa authored by Tom Evans's avatar Tom Evans Committed by tevans

update/sync with trunk (13275)

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/branches/pubsub_clustering@13276 b35dd754-fafc-0310-a699-88a17e54d16e
parent 4c4cb9b8
......@@ -32,11 +32,6 @@
<pathelement location="${basedir}/build/lib/ant-contrib.jar"/>
</classpath>
</taskdef>
<taskdef name="subdirinfo" classname="org.jivesoftware.ant.SubDirInfoTask">
<classpath>
<pathelement location="${basedir}/build/lib/ant-subdirtask.jar"/>
</classpath>
</taskdef>
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask">
<classpath>
<pathelement location="${basedir}/build/lib/xmltask.jar"/>
......@@ -338,7 +333,7 @@
</if>
</target>
<target name="plugins-dev">
<target name="plugins-dev" depends="anttasks">
<!-- Setup Openfire -->
<ant antfile="${basedir}/build/build.xml" dir="${basedir}" target="openfire"
inheritAll="false" inheritRefs="false"/>
......@@ -1206,7 +1201,7 @@
</target>
<!-- plugins =============================================================================== -->
<target name="plugins" description="Builds all plugins">
<target name="plugins" description="Builds all plugins" depends="anttasks">
<tstamp>
<format property="buildJavaDate" pattern="MMM dd, yyyy"/>
</tstamp>
......@@ -1248,7 +1243,7 @@
</for>
</target>
<target name="-plugins-impl-dev" if="plugin.dev.dir">
<target name="-plugins-impl-dev" if="plugin.dev.dir" depends="anttasks">
<!-- Get a list of plugins in the optional dev dir -->
<subdirinfo dir="${plugin.dev.dir}" property="dirlist2" ifexists="plugin.xml"/>
......@@ -1638,6 +1633,12 @@
<jar jarfile="${anttools.target.dir}/ant-subdirtask.jar">
<fileset dir="${anttools.target.dir}/classes" includes="**/*.class"/>
</jar>
<taskdef name="subdirinfo" classname="org.jivesoftware.ant.SubDirInfoTask">
<classpath>
<pathelement location="${basedir}/build/lib/ant-subdirtask.jar"/>
</classpath>
</taskdef>
</target>
<!-- clean ================================================================================= -->
......
......@@ -2,14 +2,14 @@ Source: openfire
Section: net
Priority: optional
Maintainer: Ignite Realtime Community <admin@igniterealtime.org>
Build-Depends: debhelper (>= 5), cdbs, patchutils, sun-java5-jdk, ant
Build-Depends: debhelper (>= 5), cdbs, patchutils, sun-java6-jdk | openjdk-6-jdk, ant
Standards-Version: 3.7.2
Homepage: http://www.igniterealtime.org
Package: openfire
Section: net
Priority: optional
Pre-Depends: sun-java5-jre | sun-java6-jre | default-jre-headless
Pre-Depends: sun-java5-jre | sun-java6-jre | default-jre-headless | openjdk-6-jre
Architecture: all
Description: A high performance XMPP (Jabber) server.
Openfire is an instant messaging server that implements the XMPP
......
......@@ -13,10 +13,10 @@ ETCDIR := $(DEST)/etc/openfire
LOGDIR := $(DEST)/var/log/openfire
VARDIR := $(DEST)/var/lib/openfire
JAVA_HOME := /usr/lib/jvm/java-1.5.0-sun
JAVA_HOME ?= /usr/lib/jvm/java-6-sun
DEB_ANT_BUILDFILE := build/build.xml
DEB_ANT_CLEAN_TARGET := clean
install/openfire::
cp $(TARGET)/lib/*.jar $(OPENFIRE)/lib/
cp $(TARGET)/lib/log4j.xml $(ETCDIR)
......
......@@ -143,6 +143,10 @@ public class IQvCardHandler extends IQHandler {
result.setChildElement(packet.getChildElement().createCopy());
result.setError(PacketError.Condition.item_not_found);
}
} else {
result = IQ.createResultIQ(packet);
result.setChildElement(packet.getChildElement().createCopy());
result.setError(PacketError.Condition.item_not_found);
}
}
else {
......
......@@ -23,6 +23,7 @@ package org.jivesoftware.openfire.http;
import java.io.File;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -67,6 +68,24 @@ public final class HttpBindManager {
public static final String HTTP_BIND_SECURE_PORT = "httpbind.port.secure";
public static final int HTTP_BIND_SECURE_PORT_DEFAULT = 7443;
// http binding CORS default properties
public static final String HTTP_BIND_CORS_ENABLED = "httpbind.CORS.enabled";
public static final boolean HTTP_BIND_CORS_ENABLED_DEFAULT = true;
public static final String HTTP_BIND_CORS_ALLOW_ORIGIN = "httpbind.CORS.domains";
public static final String HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT = "*";
public static final String HTTP_BIND_CORS_ALLOW_METHODS_DEFAULT = "GET, POST, OPTIONS";
public static final String HTTP_BIND_CORS_ALLOW_HEADERS_DEFAULT = "Content-Type";
public static final String HTTP_BIND_CORS_MAX_AGE_DEFAULT = "86400";
public static Map<String, Boolean> HTTP_BIND_ALLOWED_ORIGINS = new HashMap<String, Boolean>();
private static HttpBindManager instance = new HttpBindManager();
......@@ -85,6 +104,9 @@ public final class HttpBindManager {
private ContextHandlerCollection contexts;
// is all orgin allowed flag
private boolean allowAllOrigins;
public static HttpBindManager getInstance() {
return instance;
}
......@@ -96,6 +118,9 @@ public final class HttpBindManager {
PropertyEventDispatcher.addListener(new HttpServerPropertyListener());
this.httpSessionManager = new HttpSessionManager();
contexts = new ContextHandlerCollection();
// setup the cache for the allowed origins
this.setupAllowedOriginsMap();
}
public void start() {
......@@ -242,7 +267,57 @@ public final class HttpBindManager {
public String getJavaScriptUrl() {
return "http://" + XMPPServer.getInstance().getServerInfo().getXMPPDomain() + ":" +
bindPort + "/scripts/";
}
// http binding CORS support start
private void setupAllowedOriginsMap() {
String originString = getCORSAllowOrigin();
if (originString.equals(HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT)) {
allowAllOrigins = true;
} else {
allowAllOrigins = false;
String[] origins = originString.split(",");
// reset the cache
HTTP_BIND_ALLOWED_ORIGINS.clear();
for (String str : origins) {
HTTP_BIND_ALLOWED_ORIGINS.put(str, true);
}
}
}
public boolean isCORSEnabled() {
return JiveGlobals.getBooleanProperty(HTTP_BIND_CORS_ENABLED, HTTP_BIND_CORS_ENABLED_DEFAULT);
}
public void setCORSEnabled(Boolean value) {
if (value != null)
JiveGlobals.setProperty(HTTP_BIND_CORS_ENABLED, String.valueOf(value));
}
public String getCORSAllowOrigin() {
return JiveGlobals.getProperty(HTTP_BIND_CORS_ALLOW_ORIGIN , HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT);
}
public void setCORSAllowOrigin(String origins) {
if (origins == null || origins.trim().length() == 0)
origins = HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT;
else {
origins = origins.replaceAll("\\s+", "");
}
JiveGlobals.setProperty(HTTP_BIND_CORS_ALLOW_ORIGIN, origins);
setupAllowedOriginsMap();
}
public boolean isAllOriginsAllowed() {
return allowAllOrigins;
}
public boolean isThisOriginAllowed(String origin) {
return HTTP_BIND_ALLOWED_ORIGINS.get(origin) != null;
}
// http binding CORS support end
public void setHttpBindEnabled(boolean isEnabled) {
JiveGlobals.setProperty(HTTP_BIND_ENABLED, String.valueOf(isEnabled));
......
......@@ -96,6 +96,14 @@ public class HttpBindServlet extends HttpServlet {
sessionManager.stop();
}
@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Use HttpServlet's implementation to add basic headers ('Allow').
super.doOptions(request, response);
addCORSHeaders(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
......@@ -178,7 +186,7 @@ public class HttpBindServlet extends HttpServlet {
}
synchronized (session) {
try {
respond(session, response, session.getResponse((Long) request.getAttribute("request")),
respond(session, request, response, session.consumeResponse((HttpConnection) request.getAttribute("connection")),
request.getMethod());
}
catch (HttpBindException e) {
......@@ -198,7 +206,7 @@ public class HttpBindServlet extends HttpServlet {
try {
if ((session.getMajorVersion() == 1 && session.getMinorVersion() >= 6) ||
session.getMajorVersion() > 1) {
respond(session, response, createErrorBody(bindingError.getErrorType().getType(),
respond(session, request, response, createErrorBody(bindingError.getErrorType().getType(),
bindingError.getCondition()), request.getMethod());
}
else {
......@@ -261,11 +269,11 @@ public class HttpBindServlet extends HttpServlet {
if ("terminate".equals(type)) {
session.close();
respond(session, response, createEmptyBody(), request.getMethod());
respond(session, request, response, createEmptyBody(), request.getMethod());
}
else if ("true".equals(restartStream) && rootNode.elements().size() == 0) {
try {
respond(session, response, createSessionRestartResponse(session), request.getMethod());
respond(session, request, response, createSessionRestartResponse(session), request.getMethod());
}
catch (DocumentException e) {
Log.error("Error sending session restart response to client.", e);
......@@ -273,7 +281,7 @@ public class HttpBindServlet extends HttpServlet {
}
else if (pauseDuration > 0 && pauseDuration <= session.getMaxPause()) {
session.pause(pauseDuration);
respond(session, response, createEmptyBody(), request.getMethod());
respond(session, request, response, createEmptyBody(), request.getMethod());
session.setLastResponseEmpty(true);
}
else {
......@@ -281,8 +289,9 @@ public class HttpBindServlet extends HttpServlet {
connection.setContinuation(ContinuationSupport.getContinuation(request));
request.setAttribute("request-session", connection.getSession());
request.setAttribute("request", connection.getRequestId());
request.setAttribute("connection", connection);
try {
respond(session, response, session.getResponse(connection.getRequestId()),
respond(session, request, response, session.consumeResponse(connection),
request.getMethod());
}
catch (HttpBindException e) {
......@@ -324,7 +333,7 @@ public class HttpBindServlet extends HttpServlet {
if (JiveGlobals.getBooleanProperty("log.httpbind.enabled", false)) {
System.out.println(new Date()+": HTTP RECV(" + connection.getSession().getStreamID().getID() + "): " + rootNode.asXML());
}
respond(response, connection, request.getMethod());
respond(request, response, connection, request.getMethod());
}
catch (UnauthorizedException e) {
// Server wasn't initialized yet.
......@@ -337,7 +346,8 @@ public class HttpBindServlet extends HttpServlet {
}
private void respond(HttpServletResponse response, HttpConnection connection, String method)
// add request argument
private void respond(HttpServletRequest request, HttpServletResponse response, HttpConnection connection, String method)
throws IOException
{
String content;
......@@ -349,10 +359,11 @@ public class HttpBindServlet extends HttpServlet {
connection.getSession().setLastResponseEmpty(true);
}
respond(connection.getSession(), response, content, method);
respond(connection.getSession(), request, response, content, method);
}
private void respond(HttpSession session, HttpServletResponse response, String content, String method)
// add request argument
private void respond(HttpSession session, HttpServletRequest request, HttpServletResponse response, String content, String method)
throws IOException {
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("GET".equals(method) ? "text/javascript" : "text/xml");
......@@ -367,6 +378,8 @@ public class HttpBindServlet extends HttpServlet {
}
content = "_BOSH_(\"" + StringEscapeUtils.escapeJavaScript(content) + "\")";
}
addCORSHeaders(request, response);
if (JiveGlobals.getBooleanProperty("log.httpbind.enabled", false)) {
System.out.println(new Date()+": HTTP SENT(" + session.getStreamID().getID() + "): " + content);
......@@ -377,6 +390,27 @@ public class HttpBindServlet extends HttpServlet {
response.getOutputStream().close();
}
private void addCORSHeaders(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// add CORS headers
if (boshManager.isCORSEnabled()) {
if (boshManager.isAllOriginsAllowed())
// set the Access-Control-Allow-Origin header to * to allow all Origin to do the CORS
response.addHeader("Access-Control-Allow-Origin", HttpBindManager.HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT);
else {
// get the Origin header from the request and check if it is in the allowed Origin Map.
// if it is allowed write it back to the Access-Control-Allow-Origin header of the respond.
String origin = request.getHeader("Origin");
if (boshManager.isThisOriginAllowed(origin)) {
response.addHeader("Access-Control-Allow-Origin", origin);
}
}
response.addHeader("Access-Control-Allow-Methods", HttpBindManager.HTTP_BIND_CORS_ALLOW_METHODS_DEFAULT);
response.addHeader("Access-Control-Allow-Headers", HttpBindManager.HTTP_BIND_CORS_ALLOW_HEADERS_DEFAULT);
response.addHeader("Access-Control-Max-Age", HttpBindManager.HTTP_BIND_CORS_MAX_AGE_DEFAULT);
}
}
private static String createEmptyBody() {
Element body = DocumentHelper.createElement("body");
body.addNamespace("", "http://jabber.org/protocol/httpbind");
......
......@@ -123,7 +123,7 @@ public class HttpConnection {
isClosed = true;
}
if (continuation != null) {
if (continuation != null && continuation.isSuspended()) {
continuation.setAttribute("response-body", body);
continuation.resume();
session.incrementServerPacketCount();
......@@ -154,6 +154,9 @@ public class HttpConnection {
else if (body == null) {
throw new IllegalStateException("Continuation not set, cannot wait for deliverable.");
}
else if(CONNECTION_CLOSED.equals(body)) {
return null;
}
return body;
}
......@@ -199,9 +202,9 @@ public class HttpConnection {
private String waitForResponse() throws HttpBindTimeoutException {
// we enter this method when we have no messages pending delivery
// when we resume a suspended continuation, or when we time out
if (!Boolean.TRUE.equals(continuation.getAttribute(SUSPENDED))) {
continuation.setTimeout(session.getWait() * JiveConstants.SECOND);
// when we resume a suspended continuation, or when we time out
if (!Boolean.TRUE.equals(continuation.getAttribute(SUSPENDED))) {
continuation.setTimeout(session.getWait() * JiveConstants.SECOND);
continuation.suspend();
continuation.setAttribute(SUSPENDED, Boolean.TRUE);
continuation.undispatch();
......
......@@ -567,6 +567,10 @@ public class HttpSession extends LocalClientSession {
this.lastResponseEmpty = lastResponseEmpty;
}
/**
* @deprecated Doesn't make sense if we have multiple connections with the same rid in the queue.
* Use {@link #consumeResponse(HttpConnection)} instead
*/
public String getResponse(long requestID) throws HttpBindException {
for (HttpConnection connection : connectionQueue) {
if (connection.getRequestId() == requestID) {
......@@ -582,6 +586,28 @@ public class HttpSession extends LocalClientSession {
}
throw new InternalError("Could not locate connection: " + requestID);
}
/**
* Similar to {@link #getResponse(long)} but returns the response for a specific connection instance
* rather than looking up on the request id. This is because it is possible for there to be multiple
* connections in the queue for the same rid so we need to be careful that we are accessing the correct
* connection.
* <p><b>Note that this method also removes the connection from the internal connection queue.</b>
*
* @param connection the connection for which to get the response.
* @return the response from the connection
* @throws HttpBindException
*/
protected String consumeResponse(HttpConnection connection) throws HttpBindException {
Log.debug("consumeResponse: " + connection);
if(connectionQueue.contains(connection)) {
String response = getResponse(connection);
connectionQueue.remove(connection);
fireConnectionClosed(connection);
return response;
}
throw new InternalError("Could not locate connection: " + connection);
}
private String getResponse(HttpConnection connection) throws HttpBindException {
String response = null;
......@@ -685,6 +711,7 @@ public class HttpSession extends LocalClientSession {
BoshBindingError.itemNotFound);
}
connection.deliverBody(createDeliverable(deliverable.deliverables));
addConnection(connection, isPoll);
return connection;
}
else if (rid > (lastRequestID + maxRequests)) {
......@@ -714,14 +741,54 @@ public class HttpSession extends LocalClientSession {
if (connection == null) {
throw new IllegalArgumentException("Connection cannot be null.");
}
checkOveractivity(isPoll);
if (isSecure && !connection.isSecure()) {
throw new HttpBindException("Session was started from secure connection, all " +
"connections on this session must be secured.", BoshBindingError.badRequest);
}
final long rid = connection.getRequestId();
/*
* Search through the connection queue to see if this rid already exists on it. If it does then we
* will close and deliver the existing connection (if appropriate), and close and deliver the same
* deliverable on the new connection. This is under the assumption that a connection has been dropped,
* and re-requested before jetty has realised.
*/
for (HttpConnection queuedConnection : connectionQueue) {
if (queuedConnection.getRequestId() == rid) {
if(Log.isDebugEnabled()) {
Log.debug("Found previous connection in queue with rid " + rid);
}
if(queuedConnection.isClosed()) {
if(Log.isDebugEnabled()) {
Log.debug("It's closed - copying deliverables");
}
Delivered deliverable = retrieveDeliverable(rid);
if (deliverable == null) {
Log.warn("Deliverable unavailable for " + rid);
throw new HttpBindException("Unexpected RID error.",
BoshBindingError.itemNotFound);
}
connection.deliverBody(createDeliverable(deliverable.deliverables));
} else {
if(Log.isDebugEnabled()) {
Log.debug("It's still open - calling close()");
}
deliver(queuedConnection, Collections.singleton(new Deliverable("")));
connection.close();
if(rid == (lastRequestID + 1)) {
lastRequestID = rid;
}
}
break;
}
}
checkOveractivity(isPoll);
sslCertificates = connection.getPeerCertificates();
connection.setSession(this);
......
......@@ -261,6 +261,9 @@ public class LdapVCardProvider implements VCardProvider, PropertyEventListener {
username = JID.unescapeNode(username);
Map<String, String> map = getLdapAttributes(username);
Log.debug("LdapVCardProvider: Retrieving LDAP mapped vcard for " + username);
if (map.isEmpty()) {
return null;
}
Element vcard = new VCard(template).getVCard(map);
if (mergeVCard == null) {
// No vcard passed in? Hrm. Fine, return LDAP vcard.
......
......@@ -202,6 +202,44 @@ public class IQAdminHandler {
metaData.addAttribute("nick", role.getNickname());
metaData.addAttribute("affiliation", role.getAffiliation().toString());
}
} else if ("owner".equals(affiliation)) {
// The client is requesting the list of owners
Element ownerMetaData;
MUCRole role;
for (JID jid : room.getOwners()) {
ownerMetaData = result.addElement("item", "http://jabber.org/protocol/muc#admin");
ownerMetaData.addAttribute("affiliation", "owner");
ownerMetaData.addAttribute("jid", jid.toBareJID());
// Add role and nick to the metadata if the user is in the room
try {
List<MUCRole> roles = room.getOccupantsByBareJID(jid);
role = roles.get(0);
ownerMetaData.addAttribute("role", role.getRole().toString());
ownerMetaData.addAttribute("nick", role.getNickname());
}
catch (UserNotFoundException e) {
// Do nothing
}
}
} else if ("admin".equals(affiliation)) {
// The client is requesting the list of admins
Element adminMetaData;
MUCRole role;
for (JID jid : room.getAdmins()) {
adminMetaData = result.addElement("item", "http://jabber.org/protocol/muc#admin");
adminMetaData.addAttribute("affiliation", "admin");
adminMetaData.addAttribute("jid", jid.toBareJID());
// Add role and nick to the metadata if the user is in the room
try {
List<MUCRole> roles = room.getOccupantsByBareJID(jid);
role = roles.get(0);
adminMetaData.addAttribute("role", role.getRole().toString());
adminMetaData.addAttribute("nick", role.getNickname());
}
catch (UserNotFoundException e) {
// Do nothing
}
}
} else {
reply.setError(PacketError.Condition.bad_request);
}
......@@ -238,6 +276,10 @@ public class IQAdminHandler {
if ("moderator".equals(target)) {
// Add the user as a moderator of the room based on the full JID
presences.add(room.addModerator(jid, senderRole));
} else if ("owner".equals(target)) {
presences.addAll(room.addOwner(jid, senderRole));
} else if ("admin".equals(target)) {
presences.addAll(room.addAdmin(jid, senderRole));
} else if ("participant".equals(target)) {
// Add the user as a participant of the room based on the full JID
presences.add(room.addParticipant(jid,
......
......@@ -179,6 +179,9 @@ public class IQOwnerHandler {
// Create the result that will hold an item for each owner or admin
Element result = reply.setChildElement("query", "http://jabber.org/protocol/muc#owner");
// muc#owner shouldn't be used as namespace for owner and admin
// listings according to the newest versions of XEP-0045
// this code remains here for backwards compatibility
if ("owner".equals(affiliation)) {
// The client is requesting the list of owners
Element ownerMetaData;
......@@ -262,6 +265,9 @@ public class IQOwnerHandler {
try {
for (JID jid : jids.keySet()) {
String targetAffiliation = jids.get(jid);
// muc#owner shouldn't be used as namespace for owner and admin
// changes according to the newest versions of XEP-0045
// this code remains here for backwards compatibility
if ("owner".equals(targetAffiliation)) {
// Add the new user as an owner of the room
presences.addAll(room.addOwner(jid, senderRole));
......
......@@ -89,11 +89,11 @@ public class HybridUserProvider implements UserProvider {
}
}
String tertiaryClass = JiveGlobals.getXMLProperty("hybridUserProvider.tertiaryProvider.className");
if (secondaryClass != null) {
if (tertiaryClass != null) {
try {
Class c = ClassUtils.forName(secondaryClass);
Class c = ClassUtils.forName(tertiaryClass);
tertiaryProvider = (UserProvider) c.newInstance();
Log.debug("Secondary user provider: " + secondaryClass);
Log.debug("Tertiary user provider: " + tertiaryClass);
} catch (Exception e) {
Log.error("Unable to load tertiary user provider: " + tertiaryClass, e);
}
......
......@@ -62,7 +62,7 @@ public class Log {
* @deprecated replaced by {@link org.slf4j.Logger#isErrorEnabled()}.
* Functionality of this method is delegated there.
*/
@Deprecated()
@Deprecated
public static boolean isErrorEnabled() {
return Logger.isErrorEnabled();
}
......
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>GoJara 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>
Just married Plugin Changelog
</h1>
<p><b>1.0.1 </b> -- August 28, 2012</p>
<ul>
<li>Change name and mail address if necessary </li>
</ul>
<p><b>1.0.0 </b> -- July 30, 2012</p>
<ul>
<li>Initial release. </li>
</ul>
</body>
</html>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<class>org.jivesoftware.openfire.plugin.married.JustMarriedPlugin</class>
<name>Just married</name>
<description>Allows admins to rename or copy users</description>
<author>Holger Bergunde</author>
<version>1.0.1 </version>
<date>8/28/2012</date>
<minServerVersion>3.3.0</minServerVersion>
<adminconsole>
<tab id="tab-users">
<sidebar id="sidebar-users">
<item id="justmarried" name="Just married"
url="married.jsp"
description="Just married" />
</sidebar>
</tab>
</adminconsole>
</plugin>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Just married 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%;
}
#datatable TH {
color: #fff;
background-color: #2A448C;
text-align: left;
}
#datatable TD {
background-color: #FAF6EF;
}
#datatable .name {
background-color: #DCE2F5;
}
</style>
</head>
<body>
<h1>Just married Plugin Readme</h1>
<h2>Overview</h2>
<p>This plugins allows admins to rename or copy users on the local
server. By default Openfire does not allow to rename users because the
username is the primary key in database. This plugin creates a new
contact and tries to copy roster entries, groups (including shared
groups), properties and vcard from the old contact to the new renamed
one. If you want you could keep the old user as well.</p>
<h2>Installation</h2>
<p>Copy justmarried.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 remoteRoster.jar file over the
existing file.</p>
<h2>Configuration</h2>
<p>The Just married plugin can be configured under
"User/Groups"-"Users"-"Just married".</p>
</body>
</html>
\ No newline at end of file
package org.jivesoftware.openfire.plugin.married;
import java.io.File;
import java.util.List;
import org.apache.log4j.Logger;
import org.dom4j.Element;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.SharedGroupException;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.AuthFactory;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.roster.Roster;
import org.jivesoftware.openfire.roster.RosterItem;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.vcard.VCardManager;
import org.xmpp.packet.StreamError;
public class JustMarriedPlugin implements Plugin {
private static Logger Log = Logger.getLogger(JustMarriedPlugin.class);
@Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {
}
public static boolean changeName(String currentUserName, String newUserName, boolean deleteOldUser,
String newEmail, String newRealName) {
UserManager userManager = UserManager.getInstance();
try {
User currentUser = userManager.getUser(currentUserName);
// Old user found, create new one
String password = AuthFactory.getPassword(currentUserName);
String newName = (newRealName == null | newRealName.length() == 0) ? currentUser.getName() : newRealName;
String newMail = (newEmail == null | newEmail.length() == 0) ? currentUser.getEmail() : newEmail;
User newUser = userManager.createUser(newUserName, password, currentUser.getName(), newMail);
newUser.setName(newName);
newUser.setNameVisible(currentUser.isNameVisible());
newUser.setEmailVisible(currentUser.isEmailVisible());
newUser.setCreationDate(currentUser.getCreationDate());
copyRoster(currentUser, newUser, currentUserName);
copyProperties(currentUser, newUser);
copyToGroups(currentUserName, newUserName);
copyVCard(currentUserName, newUserName);
if (deleteOldUser) {
deleteUser(currentUser);
}
} catch (UserNotFoundException e) {
Log.error("Could not find user " + currentUserName, e);
return false;
} catch (UserAlreadyExistsException e) {
Log.error("Could not create user " + newUserName, e);
return false;
}
return true;
}
private static void copyVCard(String currentUserName, String newUserName) {
VCardManager vcardManager = VCardManager.getInstance();
Element vcard = vcardManager.getVCard(currentUserName);
if (vcard != null) {
try {
vcardManager.setVCard(newUserName, vcard);
} catch (Exception e) {
Log.error("Could not copy vcard to " + newUserName, e);
}
}
}
private static void copyToGroups(String currentUser, String newUser) {
GroupManager groupManager = GroupManager.getInstance();
for (Group group : groupManager.getGroups()) {
if (group.isUser(currentUser)) {
group.getMembers().add(XMPPServer.getInstance().createJID(newUser, null));
}
}
}
private static void deleteUser(User oldUser) {
UserManager.getInstance().deleteUser(oldUser);
final StreamError error = new StreamError(StreamError.Condition.not_authorized);
for (ClientSession sess : SessionManager.getInstance().getSessions(oldUser.getUsername())) {
sess.deliverRawText(error.toXML());
sess.close();
}
}
private static void copyProperties(User currentUser, User newUser) {
for (String key : currentUser.getProperties().keySet()) {
newUser.getProperties().put(key, User.getPropertyValue(currentUser.getUsername(), key));
}
}
private static void copyRoster(User currentUser, User newUser, String currentUserName) {
Roster newRoster = newUser.getRoster();
Roster currentRoster = currentUser.getRoster();
for (RosterItem item : currentRoster.getRosterItems()) {
try {
List<String> groups = item.getGroups();
RosterItem justCreated = newRoster.createRosterItem(item.getJid(), item.getNickname(), groups, true,
true);
justCreated.setAskStatus(item.getAskStatus());
justCreated.setRecvStatus(item.getRecvStatus());
justCreated.setSubStatus(item.getSubStatus());
for (Group gr : item.getSharedGroups()) {
justCreated.addSharedGroup(gr);
}
for (Group gr : item.getInvisibleSharedGroups()) {
justCreated.addInvisibleSharedGroup(gr);
}
newRoster.updateRosterItem(justCreated);
addNewUserToOthersRoster(newUser, item, currentUserName);
} catch (UserAlreadyExistsException e) {
Log.error("Could not create roster item for user " + item.getJid(), e);
} catch (SharedGroupException e) {
Log.error("Could not create roster item for user " + item.getJid()
+ " because it is a contact from a shared group", e);
} catch (UserNotFoundException e) {
Log.error("Could not update Roster item for user " + newUser.getName()
+ " because it was not properly created.", e);
}
}
}
private static void addNewUserToOthersRoster(User newUser, RosterItem otherItem, String currentUser) {
otherItem.getJid();
UserManager userManager = UserManager.getInstance();
// Is this user registered with our OF server?
String username = otherItem.getJid().getNode();
if (username != null && username.length() > 0 && userManager.isRegisteredUser(username)
&& XMPPServer.getInstance().isLocal(XMPPServer.getInstance().createJID(currentUser, null))) {
try {
User otherUser = userManager.getUser(username);
Roster otherRoster = otherUser.getRoster();
RosterItem oldUserOnOthersRoster = otherRoster.getRosterItem(XMPPServer.getInstance().createJID(
currentUser, null));
try {
if (!oldUserOnOthersRoster.isOnlyShared()) {
RosterItem justCreated = otherRoster.createRosterItem(
XMPPServer.getInstance().createJID(newUser.getUsername(), null),
oldUserOnOthersRoster.getNickname(), oldUserOnOthersRoster.getGroups(), true, true);
justCreated.setAskStatus(oldUserOnOthersRoster.getAskStatus());
justCreated.setRecvStatus(oldUserOnOthersRoster.getRecvStatus());
justCreated.setSubStatus(oldUserOnOthersRoster.getSubStatus());
otherRoster.updateRosterItem(justCreated);
}
} catch (UserAlreadyExistsException e) {
Log.error("Could not create roster item for user " + newUser.getUsername(), e);
} catch (SharedGroupException e) {
Log.error(e);
}
} catch (UserNotFoundException e) {
Log.error("Could not create roster item for user " + newUser.getUsername()
+ " because it is a contact from a shared group", e);
}
}
}
@Override
public void destroyPlugin() {
}
}
This diff is collapsed.
This diff is collapsed.
<%@ page
import="org.jivesoftware.openfire.plugin.married.JustMarriedPlugin"%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt"%>
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" />
<%
webManager.init(request, response, session, application, out);
String oldName = request.getParameter("oldName");
String newName = request.getParameter("newName");
String keepCopy = request.getParameter("copy");
String newEmail = request.getParameter("email");
String newRealName = request.getParameter("realName");
%>
<html>
<head>
<title>Just married - name changer</title>
<meta name="pageID" content="justmarried" />
<meta name="helpPage" content="" />
<script src="./js/bootstrap.min.js" type="text/javascript"></script>
<link href="./css/bootstrap.min.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="jive-contentBoxHeader">Just married</div>
<div class="jive-contentBox">
<%
if (oldName != null && newName != null && oldName.trim().length() > 0 && newName.trim().length() > 0) {
boolean success = JustMarriedPlugin.changeName(oldName, newName, keepCopy == null ? true : false, newEmail, newRealName);
if (success) {
out.write("<div class=\"success\">Sucessfully renamed user " + oldName + " to " + newName
+ "!</div>");
} else {
out.write("<div class=\"error\">Something went wrong :-/. Please have a closer look to the error log!</div>");
}
} else {
%>
<form class="form-horizontal">
<fieldset>
<legend>Change the name here</legend>
<label class="control-label" for="input01">Current username*</label>
<div
<%out.write(oldName != null && oldName.length() == 0 ? "class=\"control-group error\""
: "class=\"controls\"");%>>
<input type="text" name="oldName" style="height:26px"
class="input-xlarge"
<%out.write(oldName != null && oldName.length() == 0 ? "id=\"inputError\"" : "id=\"input01\"");%>>
<p class="help-block">The current username e.g user.name
(without server)</p>
</div>
<label class="control-label" for="input01">New username*</label>
<div
<%out.write(newName != null && newName.length() == 0 ? "class=\"control-group error\""
: "class=\"controls\"");%>>
<input type="text" name="newName" style="height:26px"
class="input-xlarge"
<%out.write(newName != null && newName.length() == 0 ? "id=\"inputError\"" : "id=\"input01\"");%>>
<p class="help-block">The new username e.g user.newname
(without server)</p>
</div>
<label class="control-label" for="input01">New E-Mail address</label>
<div class="controls">
<input type="text" name="email" style="height:26px"
class="input-xlarge" id="input01">
<p class="help-block">New email address. Will copy address from old user if field is empty.</p>
</div>
<label class="control-label" for="input01">New Name</label>
<div class="controls">
<input type="text" name="realName" style="height:26px"
class="input-xlarge" id="input01">
<p class="help-block">Will copy name from old user if field is empty.</p>
</div>
<div class="control-group">
<label class="checkbox"> <input type="checkbox"
id="optionsCheckbox2" name="copy" value="keepCopy"> Keep a
copy of the old username
</label>
</div>
<div class="control-group">
<button type="submit" class="btn btn-primary">Rename user</button>
</div>
<p class="help-block">* Mandatory item</p>
</fieldset>
</form>
<%
}
%>
</div>
</body>
</html>
......@@ -148,6 +148,11 @@ hr {
</div>
<div id="pageBody">
<h2>2.3.1 -- <span style="font-weight: normal;">July 7, 2012</span></h2>
<ul>
<li>[<a href='http://issues.igniterealtime.org/browse/OF-564'>OF-564</a>] - User import/export plugin concatenates group names in export</li>
</ul>
<h2>2.3.0 -- <span style="font-weight: normal;">February 2, 2010</span></h2>
<ul>
<li>Now requires Openfire 3.7.0.</li>
......
......@@ -37,7 +37,7 @@
<xs:element name="Item">
<xs:complexType>
<xs:sequence>
<xs:element ref="Group" maxOccurs="unbounded" />
<xs:element ref="Group" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute ref="jid" use="required" />
<xs:attribute name="askstatus" use="required" />
......
......@@ -6,8 +6,8 @@
<name>User Import Export</name>
<description>Enables import and export of user data</description>
<author>Ryan Graham</author>
<version>2.3.0</version>
<date>2/19/2010</date>
<version>2.3.1</version>
<date>7/13/2012</date>
<minServerVersion>3.7.0</minServerVersion>
<adminconsole>
......
......@@ -194,11 +194,10 @@ public class ImportExportPlugin implements Plugin {
itemElement.addAttribute("substatus", String.valueOf(ri.getSubStatus().getValue()));
itemElement.addAttribute("name", ri.getNickname());
Element groupElement = itemElement.addElement("Group");
List<String> groups = ri.getGroups();
for (String group : groups) {
if (group != null && group.trim().length() > 0) {
groupElement.addText(group);
itemElement.addElement("Group").addText(group);
}
}
}
......
......@@ -36,13 +36,21 @@
Map<String, String> errorMap = new HashMap<String, String>();
boolean isEnabled = ParamUtils.getBooleanParameter(request, "httpBindEnabled",
serverManager.isHttpBindEnabled());
// CORS
boolean isCORSEnabled = ParamUtils.getBooleanParameter(request, "CORSEnabled",
serverManager.isCORSEnabled());
if (isEnabled) {
int requestedPort = ParamUtils.getIntParameter(request, "port",
serverManager.getHttpBindUnsecurePort());
int requestedSecurePort = ParamUtils.getIntParameter(request, "securePort",
serverManager.getHttpBindSecurePort());
// CORS
String CORSDomains = ParamUtils.getParameter(request, "CORSDomains", true);
try {
serverManager.setHttpBindPorts(requestedPort, requestedSecurePort);
// CORS
serverManager.setCORSEnabled(isCORSEnabled);
serverManager.setCORSAllowOrigin(CORSDomains);
}
catch (Exception e) {
Log.error("An error has occured configuring the HTTP binding ports", e);
......@@ -71,6 +79,8 @@
int port = serverManager.getHttpBindUnsecurePort();
int securePort = serverManager.getHttpBindSecurePort();
boolean isScriptSyntaxEnabled = serverManager.isScriptSyntaxEnabled();
// CORS
boolean isCORSEnabled = serverManager.isCORSEnabled();
%>
<%@page import="org.jivesoftware.openfire.http.FlashCrossDomainServlet"%><html>
......@@ -86,6 +96,9 @@
$("securePort").disabled = !enabled;
$("rb03").disabled = !enabled;
$("rb04").disabled = !enabled;
$("rb05").disabled = !enabled;
$("rb06").disabled = !enabled;
$("CORSDomains").disabled = !enabled;
$("crossdomain").disabled = !enabled;
}
window.onload = setTimeout("setEnabled()", 500);
......@@ -198,6 +211,53 @@
</tbody>
</table>
</div>
<!-- CORS -->
<div class="jive-contentBoxHeader">Provides support for CORS (Cross-Origin Resource Sharing)</div>
<div class="jive-contentbox">
<table cellpadding="3" cellspacing="0" border="0">
<tbody>
<tr valign="top">
<td width="1%" nowrap>
<input type="radio" name="CORSEnabled" value="true" id="rb05"
<%= (isCORSEnabled ? "checked" : "") %>>
</td>
<td width="99%">
<label for="rb05">
<b>Enabled</b> - Activate CORS support for cross domain scripting
</label>
<table border="0">
<tr>
<td>
<label for="CORSDomains">
Enter domain list below separated by commas or * to allow any :
</label>
</td>
</tr>
<tr>
<td>
<input id="CORSDomains" type="text" size="80" name="CORSDomains" value="<%= serverManager.getCORSAllowOrigin() %>">
</td>
</tr>
</table>
</td>
</tr>
<tr valign="top">
<td width="1%" nowrap>
<input type="radio" name="CORSEnabled" value="false" id="rb06"
<%= (!isCORSEnabled ? "checked" : "") %>>
</td>
<td width="99%">
<label for="rb06">
<b>Disabled</b> - Disable CORS support
</label>
</td>
</tr>
</tbody>
</table>
</div>
<!-- CORS -->
<div class="jive-contentBoxHeader">Cross-domain policy</div>
<div class="jive-contentbox">
<p><fmt:message key="httpbind.settings.crossdomain.info.general" /></p>
......
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