Commit 2d5c65c3 authored by wroot's avatar wroot

Merge remote-tracking branch 'igniterealtime/master'

parents 4904a1ac b1fa4367
...@@ -9,7 +9,7 @@ Homepage: http://www.igniterealtime.org ...@@ -9,7 +9,7 @@ Homepage: http://www.igniterealtime.org
Package: openfire Package: openfire
Section: net Section: net
Priority: optional Priority: optional
Pre-Depends: default-jre-headless (>= 1.7) Pre-Depends: default-jre-headless (>= 1.7) | java7-runtime-headless | java7-runtime | java8-runtime-headless | java8-runtime | java9-runtime-headless | java9-runtime
Architecture: all Architecture: all
Description: A high performance XMPP (Jabber) server. Description: A high performance XMPP (Jabber) server.
Openfire is an instant messaging server that implements the XMPP Openfire is an instant messaging server that implements the XMPP
......
...@@ -120,10 +120,6 @@ ...@@ -120,10 +120,6 @@
<classpathentry kind="lib" path="src/plugins/registration/lib/recaptcha4j.jar"/> <classpathentry kind="lib" path="src/plugins/registration/lib/recaptcha4j.jar"/>
<classpathentry kind="lib" path="src/plugins/sip/lib/JainSipApi1.1.jar"/> <classpathentry kind="lib" path="src/plugins/sip/lib/JainSipApi1.1.jar"/>
<classpathentry kind="lib" path="src/plugins/sip/lib/nist-sip-1.2.jar"/> <classpathentry kind="lib" path="src/plugins/sip/lib/nist-sip-1.2.jar"/>
<classpathentry kind="lib" path="src/plugins/userImportExport/lib/isorelax.jar"/>
<classpathentry kind="lib" path="src/plugins/userImportExport/lib/msv.jar"/>
<classpathentry kind="lib" path="src/plugins/userImportExport/lib/relaxngDatatype.jar"/>
<classpathentry kind="lib" path="src/plugins/userImportExport/lib/xsdlib.jar"/>
<classpathentry kind="lib" path="src/test/throttletest/build/lib/smack.jar"/> <classpathentry kind="lib" path="src/test/throttletest/build/lib/smack.jar"/>
<classpathentry kind="lib" path="src/test/throttletest/build/lib/smackx.jar"/> <classpathentry kind="lib" path="src/test/throttletest/build/lib/smackx.jar"/>
<classpathentry kind="lib" path="src/web/WEB-INF/lib/commons-fileupload.jar"/> <classpathentry kind="lib" path="src/web/WEB-INF/lib/commons-fileupload.jar"/>
...@@ -178,5 +174,6 @@ ...@@ -178,5 +174,6 @@
<classpathentry kind="lib" path="src/plugins/jmxweb/lib/quartz-2.2.2.jar"/> <classpathentry kind="lib" path="src/plugins/jmxweb/lib/quartz-2.2.2.jar"/>
<classpathentry kind="lib" path="src/plugins/jmxweb/lib/genson-1.4.jar"/> <classpathentry kind="lib" path="src/plugins/jmxweb/lib/genson-1.4.jar"/>
<classpathentry kind="lib" path="src/plugins/jmxweb/lib/itextpdf-5.4.1.jar"/> <classpathentry kind="lib" path="src/plugins/jmxweb/lib/itextpdf-5.4.1.jar"/>
<classpathentry kind="lib" path="build/lib/merge/jsmpp.jar"/>
<classpathentry kind="output" path="work/classes"/> <classpathentry kind="output" path="work/classes"/>
</classpath> </classpath>
...@@ -467,7 +467,7 @@ ...@@ -467,7 +467,7 @@
</uninstallerStartup> </uninstallerStartup>
</installerGui> </installerGui>
<mediaSets> <mediaSets>
<win32 name="Windows" id="3" mediaFileName="" installDir="${compiler:WINDOWS_INSTALL_DIR}" overridePrincipalLanguage="true" requires64bit="false" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" includedJRE="windows-x86-1.8.0_102" manualJREEntry="false" bundleType="1" jreURL="" jreFtpURL="" jreShared="false" customInstallBaseDir="" createUninstallIcon="true" contentFilesType="1" downloadURL="" runAsAdmin="false"> <win32 name="Windows" id="3" mediaFileName="" installDir="${compiler:WINDOWS_INSTALL_DIR}" overridePrincipalLanguage="true" requires64bit="false" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" includedJRE="windows-x86-1.8.0_112" manualJREEntry="false" bundleType="1" jreURL="" jreFtpURL="" jreShared="false" customInstallBaseDir="" createUninstallIcon="true" contentFilesType="1" downloadURL="" runAsAdmin="false">
<excludedLaunchers> <excludedLaunchers>
<launcher id="22" /> <launcher id="22" />
</excludedLaunchers> </excludedLaunchers>
...@@ -484,7 +484,7 @@ ...@@ -484,7 +484,7 @@
<excludedInstallerScreens /> <excludedInstallerScreens />
<excludedUninstallerScreens /> <excludedUninstallerScreens />
</win32> </win32>
<linuxRPM name="Linux RPM" id="18" mediaFileName="" installDir="/opt/${compiler:UNIX_INSTALL_DIR}" overridePrincipalLanguage="true" requires64bit="false" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" includedJRE="linux-x86-1.8.0_102" manualJREEntry="false" os="linux" arch="i386"> <linuxRPM name="Linux RPM" id="18" mediaFileName="" installDir="/opt/${compiler:UNIX_INSTALL_DIR}" overridePrincipalLanguage="true" requires64bit="false" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" includedJRE="linux-x86-1.8.0_112" manualJREEntry="false" os="linux" arch="i386">
<excludedLaunchers> <excludedLaunchers>
<launcher id="2" /> <launcher id="2" />
<launcher id="12" /> <launcher id="12" />
......
...@@ -10,6 +10,7 @@ cglib.jar | 2.1.3 (JMock 2.1.0) ...@@ -10,6 +10,7 @@ cglib.jar | 2.1.3 (JMock 2.1.0)
commons-lang.jar | 2.3 | Apache 2.0 commons-lang.jar | 2.3 | Apache 2.0
commons-httpclient.jar | 3.1 | Apache 2.0 commons-httpclient.jar | 3.1 | Apache 2.0
commons-codec.jar | 1.9 | Apache 2.0 commons-codec.jar | 1.9 | Apache 2.0
commons-pool2.jar | 2.4.2 | Apache 2.0
dom4j.jar | 1.6.1 | BSD (http://www.dom4j.org/dom4j-1.6.1/license.html) dom4j.jar | 1.6.1 | BSD (http://www.dom4j.org/dom4j-1.6.1/license.html)
concurrentlinkedhashmap-lru | concurrentlinkedhashmap-lru-1.0_jdk5 | Apache 2.0 concurrentlinkedhashmap-lru | concurrentlinkedhashmap-lru-1.0_jdk5 | Apache 2.0
dbutil.jar | Jive Code, no release version. | GPL dbutil.jar | Jive Code, no release version. | GPL
...@@ -54,6 +55,7 @@ jmdns.jar | PRE 1.0, patched ...@@ -54,6 +55,7 @@ jmdns.jar | PRE 1.0, patched
jmock.jar | 2.1.0 | jmock.jar | 2.1.0 |
jmock-junit4.jar | 2.1.0 | jmock-junit4.jar | 2.1.0 |
jmock-legacy.jar | 2.1.0 | jmock-legacy.jar | 2.1.0 |
jsmpp | 2.2.4 | Apache 2.0
jtds.jar | 1.3.1 | LGPL jtds.jar | 1.3.1 | LGPL
junit.jar | 4.11 | EPL 1.0 junit.jar | 4.11 | EPL 1.0
hamcrest-core.jar | 1.3 (required by junit) | new BSD licence hamcrest-core.jar | 1.3 (required by junit) | new BSD licence
......
...@@ -7,6 +7,11 @@ Source0: %{OPENFIRE_SOURCE} ...@@ -7,6 +7,11 @@ Source0: %{OPENFIRE_SOURCE}
%ifnarch noarch %ifnarch noarch
Source1: %{JRE_BUNDLE} Source1: %{JRE_BUNDLE}
%endif %endif
%ifarch noarch
# Note that epoch is set here to 1, this appears to be consistent with non-Redhat
# jres as well due to an ancient problem with java-1.5.0-ibm jpackage RPM
Requires: java-headless >= 1:1.7.0
%endif
Group: Applications/Communications Group: Applications/Communications
Vendor: Igniterealtime Community Vendor: Igniterealtime Community
Packager: Igniterealtime Community Packager: Igniterealtime Community
......
...@@ -150,6 +150,28 @@ ...@@ -150,6 +150,28 @@
&lt;/jive&gt; &lt;/jive&gt;
</pre> </pre>
<p>Another option is to use an AdminProvider. AdminProvider instances are responsible for listing
the administrators users dynamically. The default use the <tt>authorizedUsernames</tt> setting
previously explained. JDBCAdminProvider allows to list the administrators from a SQL query.
For example:</p>
<pre>
&lt;jive&gt;
...
&lt;provider&gt;
...
&lt;admin&gt;
&lt;className&gt;org.jivesoftware.openfire.admin.JDBCAdminProvider&lt;/className&gt;
&lt;/admin&gt;
...
&lt;/provider&gt;
&lt;jdbcAdminProvider&gt;
&lt;getAdminsSQL&gt;SELECT userid FROM user_account WHERE administrator='Y'&lt;/getAdminsSQL&gt;
&lt;/jdbcAdminProvider&gt;
...
&lt;/jive&gt;
</pre>
<h3>User Integration</h3> <h3>User Integration</h3>
<p>Optionally, Openfire can load user data from your custom database. If you enable user integration <p>Optionally, Openfire can load user data from your custom database. If you enable user integration
......
...@@ -290,7 +290,7 @@ muc.form.conf.anyone=Jeder ...@@ -290,7 +290,7 @@ muc.form.conf.anyone=Jeder
muc.form.conf.owner_enablelogging=Raumkonversationen mitschreiben muc.form.conf.owner_enablelogging=Raumkonversationen mitschreiben
muc.form.conf.owner_reservednick=Nur Teilnehmer mit registrierten Nicknames Zutritt gew\u00e4hren muc.form.conf.owner_reservednick=Nur Teilnehmer mit registrierten Nicknames Zutritt gew\u00e4hren
muc.form.conf.owner_canchangenick=Teilnehmern erlauben Ihren Nickname zu \u00e4ndern muc.form.conf.owner_canchangenick=Teilnehmern erlauben Ihren Nickname zu \u00e4ndern
muc.form.conf.owner_registration=Benuter erlauben sich beim Raum zu registrieren muc.form.conf.owner_registration=Benutzer erlauben sich beim Raum zu registrieren
muc.form.conf.roomadminsfixed=Administratoren f\u00fcr diesen Raum k\u00f6nnen angegeben werden. \ muc.form.conf.roomadminsfixed=Administratoren f\u00fcr diesen Raum k\u00f6nnen angegeben werden. \
Bitte eine JID pro Zeile angeben. Bitte eine JID pro Zeile angeben.
muc.form.conf.owner_roomadmins=Raumadministratoren muc.form.conf.owner_roomadmins=Raumadministratoren
......
...@@ -496,6 +496,8 @@ tab.server.descr=Click to manage server settings ...@@ -496,6 +496,8 @@ tab.server.descr=Click to manage server settings
sidebar.manage-updates.descr=Click to manage server or plugins updates sidebar.manage-updates.descr=Click to manage server or plugins updates
sidebar.server-email=Email Settings sidebar.server-email=Email Settings
sidebar.server-email.descr=Click to configure email settings sidebar.server-email.descr=Click to configure email settings
sidebar.server-sms=SMS Settings
sidebar.server-sms.descr=Click to configure SMS settings
sidebar.security-audit-viewer=Security Audit Viewer sidebar.security-audit-viewer=Security Audit Viewer
sidebar.security-audit-viewer.descr=Click to view the security audit logs sidebar.security-audit-viewer.descr=Click to view the security audit logs
sidebar.sidebar-server-settings=Server Settings sidebar.sidebar-server-settings=Server Settings
...@@ -2614,7 +2616,7 @@ system.email.info=Use the form below to set the host and port of your email serv ...@@ -2614,7 +2616,7 @@ system.email.info=Use the form below to set the host and port of your email serv
on your mail server. Note, if you choose to enable mail debugging the debug output will be written \ on your mail server. Note, if you choose to enable mail debugging the debug output will be written \
to your appserver&#39;s standard out log. to your appserver&#39;s standard out log.
system.email.update_success=SMTP settings updated successfully. system.email.update_success=SMTP settings updated successfully.
system.email.update_failure=An error occured. Please verify that you have filled out all required fields \ system.email.update_failure=An error occurred. Please verify that you have filled out all required fields \
correctly and try again. correctly and try again.
system.email.name=SMTP Settings system.email.name=SMTP Settings
system.email.mail_host=Mail Host system.email.mail_host=Mail Host
...@@ -2650,6 +2652,43 @@ system.emailtest.body=Body ...@@ -2650,6 +2652,43 @@ system.emailtest.body=Body
system.emailtest.send=Send system.emailtest.send=Send
system.emailtest.cancel=Cancel/Go Back system.emailtest.cancel=Cancel/Go Back
# System SMS
system.sms.title=SMS Settings
system.sms.info=Use the form below to configure the connection to your SMS server. The SMPP protocol will be used to \
establish a connection. At a minimum, you should provide the host address of the SMPP server, your systemId \
(which can be thought of as your username) the corresponding password and an optional system type, all of \
which should be supplied to you by your SMPP service provider.
system.sms.update_success=Settings updated successfully.
system.sms.config_failure=Configuration failure. Please verify that you have filled out all required fields \
correctly and try again.
system.sms.name=SMPP Settings
system.sms.host=Host Address
system.sms.valid_host=Please enter a valid host address.
system.sms.port=Port (Optional)
system.sms.valid_port=Please enter a valid port number (between 1 and 65535).
system.sms.systemId=SystemId (username)
system.sms.valid_systemId=Please enter a valid systemId value.
system.sms.password=Password
system.sms.valid_password=Please enter a valid password.
system.sms.systemType=System Type (Optional)
system.sms.valid_systemType=Please enter a valid system type.
system.sms.save=Save Changes
system.sms.send_test=Send Test SMS...
system.smstest.title=SMS Settings
system.smstest.success=SMS was successfully sent. Verify it was sent by checking the SMS inbox of the recipient.
system.smstest.failure=An error occurred while trying to send the SMS message. The error message: <i>{0}</i>.
system.smstest.info=Use the form below to send a test message. Note that SMS does not guarantee speedy delivery, but \
generally, your message should arrive within moments.
system.smstest.invalid-service-config=The SMS service has not been configured properly. Please go back to the {0}SMS \
settings page{1} and address all issues presented there.
system.smstest.recipient=Recipient
system.smstest.valid_recipient=Please enter a recipient (phone number)
system.smstest.message=Message
system.smstest.valid_message=Please provide a short message.
system.smstest.send=Send
system.smstest.cancel=Cancel/Go Back
# File Transfer Proxy # File Transfer Proxy
filetransferproxy.settings.title=File Transfer Proxy Settings filetransferproxy.settings.title=File Transfer Proxy Settings
......
/**
* $Revision$
* $Date$
*
* Copyright (C) 2005-2008 Jive Software. All rights reserved.
* Copyright 2008-2016 Robert Marcano
*
* Licensed 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.jivesoftware.openfire.admin;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
/**
* The JDBC admin provider allows you to use an external database to define the administrators
* users. It is best used with the JDBCAuthProvider & JDBCGroupProvider to provide integration
* between your external system and Openfire. All data is treated as read-only so any
* set operations will result in an exception.<p/>
*
* To enable this provider, set the following in the system properties:<p/>
*
* <ul>
* <li><tt>provider.admin.className = org.jivesoftware.openfire.admin.JDBCAdminProvider</tt></li>
* </ul>
*
* Then you need to set your driver, connection string and SQL statements:
* <p/>
* <ul>
* <li><tt>jdbcProvider.driver = com.mysql.jdbc.Driver</tt></li>
* <li><tt>jdbcProvider.connectionString = jdbc:mysql://localhost/dbname?user=username&amp;password=secret</tt></li>
* <li><tt>jdbcAdminProvider.getAdminsSQL = SELECT user FROM myAdmins</tt></li>
* </ul>
*
* In order to use the configured JDBC connection provider do not use a JDBC
* connection string, set the following property
*
* <ul>
* <li><tt>jdbcAdminProvider.useConnectionProvider = true</tt></li>
* </ul>
*
*
* @author Robert Marcano
*/
public class JDBCAdminProvider implements AdminProvider {
private static final Logger Log = LoggerFactory.getLogger(JDBCAdminProvider.class);
private final String getAdminsSQL;
private final String xmppDomain;
private final boolean useConnectionProvider;
private String connectionString;
/**
* Constructs a new JDBC admin provider.
*/
public JDBCAdminProvider() {
// Convert XML based provider setup to Database based
JiveGlobals.migrateProperty("jdbcProvider.driver");
JiveGlobals.migrateProperty("jdbcProvider.connectionString");
JiveGlobals.migrateProperty("jdbcAdminProvider.getAdminsSQL");
xmppDomain = JiveGlobals.getProperty("xmpp.domain");
useConnectionProvider = JiveGlobals.getBooleanProperty("jdbcAdminProvider.useConnectionProvider");
// Load database statement for reading admin list
getAdminsSQL = JiveGlobals.getProperty("jdbcAdminProvider.getAdminsSQL");
// Load the JDBC driver and connection string
if (!useConnectionProvider) {
String jdbcDriver = JiveGlobals.getProperty("jdbcProvider.driver");
try {
Class.forName(jdbcDriver).newInstance();
}
catch (Exception e) {
Log.error("Unable to load JDBC driver: " + jdbcDriver, e);
return;
}
connectionString = JiveGlobals.getProperty("jdbcProvider.connectionString");
}
}
@Override
public List<JID> getAdmins() {
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<JID> jids = new ArrayList<>();
try {
con = getConnection();
pstmt = con.prepareStatement(getAdminsSQL);
rs = pstmt.executeQuery();
while (rs.next()) {
String name = rs.getString(1);
jids.add(new JID(name + "@" + xmppDomain));
}
return jids;
}
catch (SQLException e) {
throw new RuntimeException(e);
}
finally {
DbConnectionManager.closeConnection(rs, pstmt, con);
}
}
@Override
public void setAdmins(List<JID> admins) {
// Reject the operation since the provider is read-only
throw new UnsupportedOperationException();
}
@Override
public boolean isReadOnly() {
return true;
}
private Connection getConnection() throws SQLException {
if (useConnectionProvider) {
return DbConnectionManager.getConnection();
}
return DriverManager.getConnection(connectionString);
}
}
...@@ -154,7 +154,7 @@ public class PluginMonitor ...@@ -154,7 +154,7 @@ public class PluginMonitor
if ( Files.exists( dir ) && Files.getLastModifiedTime( jarFile ).toMillis() > Files.getLastModifiedTime( dir ).toMillis() ) if ( Files.exists( dir ) && Files.getLastModifiedTime( jarFile ).toMillis() > Files.getLastModifiedTime( dir ).toMillis() )
{ {
// If this is the first time that the monitor process is running, then plugins won't be loaded yet. Therefore, just delete the directory. // If this is the first time that the monitor process is running, then plugins won't be loaded yet. Therefore, just delete the directory.
if ( pluginManager.isExecuted() ) if ( !pluginManager.isExecuted() )
{ {
int count = 0; int count = 0;
// Attempt to delete the folder for up to 5 seconds. // Attempt to delete the folder for up to 5 seconds.
......
...@@ -51,7 +51,7 @@ public class IQPrivacyHandler extends IQHandler ...@@ -51,7 +51,7 @@ public class IQPrivacyHandler extends IQHandler
private IQHandlerInfo info; private IQHandlerInfo info;
private PrivacyListManager manager = PrivacyListManager.getInstance(); private PrivacyListManager manager = PrivacyListManager.getInstance();
private PrivacyListProvider provider = new PrivacyListProvider(); private PrivacyListProvider provider = PrivacyListProvider.getInstance();
public IQPrivacyHandler() { public IQPrivacyHandler() {
super("Blocking Communication Handler"); super("Blocking Communication Handler");
......
...@@ -542,6 +542,7 @@ public final class HttpBindManager { ...@@ -542,6 +542,7 @@ public final class HttpBindManager {
initializers.add(new ContainerInitializer(new JasperInitializer(), null)); initializers.add(new ContainerInitializer(new JasperInitializer(), null));
context.setAttribute("org.eclipse.jetty.containerInitializers", initializers); context.setAttribute("org.eclipse.jetty.containerInitializers", initializers);
context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager()); context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager());
context.setAllowNullPathInfo(true);
context.addServlet(new ServletHolder(new HttpBindServlet()),"/*"); context.addServlet(new ServletHolder(new HttpBindServlet()),"/*");
if (isHttpCompressionEnabled()) { if (isHttpCompressionEnabled()) {
Filter gzipFilter = new AsyncGzipFilter() { Filter gzipFilter = new AsyncGzipFilter() {
...@@ -573,6 +574,7 @@ public final class HttpBindManager { ...@@ -573,6 +574,7 @@ public final class HttpBindManager {
initializers.add(new ContainerInitializer(new JasperInitializer(), null)); initializers.add(new ContainerInitializer(new JasperInitializer(), null));
context.setAttribute("org.eclipse.jetty.containerInitializers", initializers); context.setAttribute("org.eclipse.jetty.containerInitializers", initializers);
context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager()); context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager());
context.setAllowNullPathInfo(true);
context.addServlet(new ServletHolder(new FlashCrossDomainServlet()),""); context.addServlet(new ServletHolder(new FlashCrossDomainServlet()),"");
} }
......
...@@ -249,7 +249,7 @@ public class LockOutManager { ...@@ -249,7 +249,7 @@ public class LockOutManager {
// If group wan't found in cache, load it up and put it there. // If group wan't found in cache, load it up and put it there.
if (flag == null) { if (flag == null) {
flag = provider.getDisabledStatus(username); flag = provider.getDisabledStatus(username);
lockOutCache.put(username, flag); if (flag != null) lockOutCache.put(username, flag);
} }
} }
} }
......
...@@ -162,16 +162,7 @@ public class LocalMUCRole implements MUCRole { ...@@ -162,16 +162,7 @@ public class LocalMUCRole implements MUCRole {
} }
this.presence = newPresence; this.presence = newPresence;
this.presence.setFrom(getRoleAddress()); this.presence.setFrom(getRoleAddress());
if (extendedInformation != null) { updatePresence();
// Remove any previous extendedInformation, then re-add it.
Element mucUser = presence.getElement().element(QName.get("x", "http://jabber.org/protocol/muc#user"));
if (mucUser != null) {
// Remove any previous extendedInformation, then re-add it.
presence.getElement().remove(mucUser);
}
Element exi = extendedInformation.createCopy();
presence.getElement().add(exi);
}
} }
@Override @Override
...@@ -297,8 +288,21 @@ public class LocalMUCRole implements MUCRole { ...@@ -297,8 +288,21 @@ public class LocalMUCRole implements MUCRole {
ElementUtil.setProperty(extendedInformation, "x.item:jid", user.getAddress().toString()); ElementUtil.setProperty(extendedInformation, "x.item:jid", user.getAddress().toString());
ElementUtil.setProperty(extendedInformation, "x.item:affiliation", affiliation.toString()); ElementUtil.setProperty(extendedInformation, "x.item:affiliation", affiliation.toString());
ElementUtil.setProperty(extendedInformation, "x.item:role", role.toString()); ElementUtil.setProperty(extendedInformation, "x.item:role", role.toString());
updatePresence();
} }
private void updatePresence() {
if (extendedInformation != null && presence != null) {
// Remove any previous extendedInformation, then re-add it.
Element mucUser = presence.getElement().element(QName.get("x", "http://jabber.org/protocol/muc#user"));
if (mucUser != null) {
// Remove any previous extendedInformation, then re-add it.
presence.getElement().remove(mucUser);
}
Element exi = extendedInformation.createCopy();
presence.getElement().add(exi);
}
}
@Override @Override
public int hashCode() { public int hashCode() {
......
...@@ -1393,8 +1393,6 @@ public class LocalMUCRoom implements MUCRoom, GroupEventListener { ...@@ -1393,8 +1393,6 @@ public class LocalMUCRoom implements MUCRoom, GroupEventListener {
if (role.isLocal()) { if (role.isLocal()) {
role.setAffiliation(newAffiliation); role.setAffiliation(newAffiliation);
role.setRole(newRole); role.setRole(newRole);
// Set the new presence, so that the updated affiliation and role is reflected in the presence stanza.
role.setPresence(role.getPresence());
// Notify the other cluster nodes to update the occupant // Notify the other cluster nodes to update the occupant
CacheFactory.doClusterTask(new UpdateOccupant(this, role)); CacheFactory.doClusterTask(new UpdateOccupant(this, role));
// Prepare a new presence to be sent to all the room occupants // Prepare a new presence to be sent to all the room occupants
......
...@@ -20,43 +20,20 @@ ...@@ -20,43 +20,20 @@
package org.jivesoftware.openfire.muc.spi; package org.jivesoftware.openfire.muc.spi;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.QName;
import org.jivesoftware.openfire.PacketException; import org.jivesoftware.openfire.PacketException;
import org.jivesoftware.openfire.PacketRouter; import org.jivesoftware.openfire.PacketRouter;
import org.jivesoftware.openfire.auth.UnauthorizedException; import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.muc.CannotBeInvitedException; import org.jivesoftware.openfire.muc.*;
import org.jivesoftware.openfire.muc.ConflictException;
import org.jivesoftware.openfire.muc.ForbiddenException;
import org.jivesoftware.openfire.muc.HistoryRequest;
import org.jivesoftware.openfire.muc.MUCRole;
import org.jivesoftware.openfire.muc.MUCRoom;
import org.jivesoftware.openfire.muc.MUCUser;
import org.jivesoftware.openfire.muc.MultiUserChatService;
import org.jivesoftware.openfire.muc.NotAcceptableException;
import org.jivesoftware.openfire.muc.NotAllowedException;
import org.jivesoftware.openfire.muc.RegistrationRequiredException;
import org.jivesoftware.openfire.muc.RoomLockedException;
import org.jivesoftware.openfire.muc.ServiceUnavailableException;
import org.jivesoftware.openfire.user.UserAlreadyExistsException; import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.util.LocaleUtils; import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.NotFoundException; import org.jivesoftware.util.NotFoundException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ; import org.xmpp.packet.*;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import java.util.*;
import org.xmpp.packet.Packet; import java.util.concurrent.ConcurrentHashMap;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.Presence;
/** /**
* Representation of users interacting with the chat service. A user * Representation of users interacting with the chat service. A user
...@@ -384,10 +361,14 @@ public class LocalMUCUser implements MUCUser { ...@@ -384,10 +361,14 @@ public class LocalMUCUser implements MUCUser {
// Packets to a specific node/group/room // Packets to a specific node/group/room
MUCRole role = roles.get(group); MUCRole role = roles.get(group);
if (role == null) { if (role == null) {
// If a non-occupant sends a disco to an address of the form <room@service/nick>, Log.debug( "Ignoring stanza received from a non-occupant of '{}': {}", group, packet.toXML() );
// a MUC service MUST return a <bad-request/> error. if ( packet.isRequest() )
// http://xmpp.org/extensions/xep-0045.html#disco-occupant {
sendErrorPacket(packet, PacketError.Condition.bad_request); // If a non-occupant sends a disco to an address of the form <room@service/nick>,
// a MUC service MUST return a <bad-request/> error.
// http://xmpp.org/extensions/xep-0045.html#disco-occupant
sendErrorPacket( packet, PacketError.Condition.bad_request );
}
} }
else if (IQ.Type.result == packet.getType() else if (IQ.Type.result == packet.getType()
|| IQ.Type.error == packet.getType()) { || IQ.Type.error == packet.getType()) {
......
...@@ -34,7 +34,7 @@ public class PrivacyListManager { ...@@ -34,7 +34,7 @@ public class PrivacyListManager {
private static final PrivacyListManager instance = new PrivacyListManager(); private static final PrivacyListManager instance = new PrivacyListManager();
private static Cache<String, PrivacyList> listsCache; private static Cache<String, PrivacyList> listsCache;
private PrivacyListProvider provider = new PrivacyListProvider(); private PrivacyListProvider provider = PrivacyListProvider.getInstance();
private List<PrivacyListEventListener> listeners = new CopyOnWriteArrayList<>(); private List<PrivacyListEventListener> listeners = new CopyOnWriteArrayList<>();
......
...@@ -29,7 +29,7 @@ import java.util.HashMap; ...@@ -29,7 +29,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicBoolean;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.io.SAXReader; import org.dom4j.io.SAXReader;
...@@ -45,6 +45,8 @@ import org.slf4j.LoggerFactory; ...@@ -45,6 +45,8 @@ import org.slf4j.LoggerFactory;
*/ */
public class PrivacyListProvider { public class PrivacyListProvider {
private static final PrivacyListProvider instance = new PrivacyListProvider();
private static final Logger Log = LoggerFactory.getLogger(PrivacyListProvider.class); private static final Logger Log = LoggerFactory.getLogger(PrivacyListProvider.class);
private static final String PRIVACY_LIST_COUNT = private static final String PRIVACY_LIST_COUNT =
...@@ -71,11 +73,20 @@ public class PrivacyListProvider { ...@@ -71,11 +73,20 @@ public class PrivacyListProvider {
private BlockingQueue<SAXReader> xmlReaders = new LinkedBlockingQueue<>(POOL_SIZE); private BlockingQueue<SAXReader> xmlReaders = new LinkedBlockingQueue<>(POOL_SIZE);
/** /**
* Stores the total number of privacy lists. * Boolean used to optimize getters when the database is empty
*/ */
private AtomicInteger privacyListCount; private AtomicBoolean databaseContainsPrivacyLists;
public PrivacyListProvider() { /**
* Returns the unique instance of this class.
*
* @return the unique instance of this class.
*/
public static PrivacyListProvider getInstance() {
return instance;
}
private PrivacyListProvider() {
super(); super();
// Initialize the pool of sax readers // Initialize the pool of sax readers
for (int i=0; i<POOL_SIZE; i++) { for (int i=0; i<POOL_SIZE; i++) {
...@@ -84,13 +95,10 @@ public class PrivacyListProvider { ...@@ -84,13 +95,10 @@ public class PrivacyListProvider {
xmlReaders.add(xmlReader); xmlReaders.add(xmlReader);
} }
// Load the total number of privacy lists in the database. We're looking // Checks if the PrivacyLists database is empty.
// for the (very common) special case that there are no privacy lists stored. // In that case, we can optimize away many database calls.
// In that case, we can optimize away many database calls. In the future, a databaseContainsPrivacyLists = new AtomicBoolean(false);
// better general-case solution may be to cache all privacy lists defined loadDatabaseContainsPrivacyLists();
// if there are less than, say, 500.
privacyListCount = new AtomicInteger(0);
loadPrivacyListCount();
} }
/** /**
...@@ -102,7 +110,7 @@ public class PrivacyListProvider { ...@@ -102,7 +110,7 @@ public class PrivacyListProvider {
*/ */
public Map<String, Boolean> getPrivacyLists(String username) { public Map<String, Boolean> getPrivacyLists(String username) {
// If there are no privacy lists stored, this method is a no-op. // If there are no privacy lists stored, this method is a no-op.
if (privacyListCount.get() == 0) { if (!databaseContainsPrivacyLists.get()) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
...@@ -139,7 +147,7 @@ public class PrivacyListProvider { ...@@ -139,7 +147,7 @@ public class PrivacyListProvider {
*/ */
public PrivacyList loadPrivacyList(String username, String listName) { public PrivacyList loadPrivacyList(String username, String listName) {
// If there are no privacy lists stored, this method is a no-op. // If there are no privacy lists stored, this method is a no-op.
if (privacyListCount.get() == 0) { if (!databaseContainsPrivacyLists.get()) {
return null; return null;
} }
...@@ -203,7 +211,7 @@ public class PrivacyListProvider { ...@@ -203,7 +211,7 @@ public class PrivacyListProvider {
*/ */
public PrivacyList loadDefaultPrivacyList(String username) { public PrivacyList loadDefaultPrivacyList(String username) {
// If there are no privacy lists stored, this method is a no-op. // If there are no privacy lists stored, this method is a no-op.
if (privacyListCount.get() == 0) { if (!databaseContainsPrivacyLists.get()) {
return null; return null;
} }
...@@ -280,9 +288,7 @@ public class PrivacyListProvider { ...@@ -280,9 +288,7 @@ public class PrivacyListProvider {
finally { finally {
DbConnectionManager.closeConnection(pstmt, con); DbConnectionManager.closeConnection(pstmt, con);
} }
// Set the privacy list count to -1. We don't know how many privacy lists there databaseContainsPrivacyLists.set(true);
// are, but it's not "0", which is the case we care about.
privacyListCount.set(-1);
} }
/** /**
...@@ -310,6 +316,7 @@ public class PrivacyListProvider { ...@@ -310,6 +316,7 @@ public class PrivacyListProvider {
finally { finally {
DbConnectionManager.closeConnection(pstmt, con); DbConnectionManager.closeConnection(pstmt, con);
} }
databaseContainsPrivacyLists.set(true);
} }
/** /**
...@@ -320,7 +327,7 @@ public class PrivacyListProvider { ...@@ -320,7 +327,7 @@ public class PrivacyListProvider {
*/ */
public void deletePrivacyList(String username, String listName) { public void deletePrivacyList(String username, String listName) {
// If there are no privacy lists stored, this method is a no-op. // If there are no privacy lists stored, this method is a no-op.
if (privacyListCount.get() == 0) { if (!databaseContainsPrivacyLists.get()) {
return; return;
} }
Connection con = null; Connection con = null;
...@@ -338,9 +345,7 @@ public class PrivacyListProvider { ...@@ -338,9 +345,7 @@ public class PrivacyListProvider {
finally { finally {
DbConnectionManager.closeConnection(pstmt, con); DbConnectionManager.closeConnection(pstmt, con);
} }
// Set the privacy list count to -1. We don't know how many privacy lists there databaseContainsPrivacyLists.set(true);
// are, but it's probably not "0", which is the case we care about.
privacyListCount.set(-1);
} }
/** /**
...@@ -350,7 +355,7 @@ public class PrivacyListProvider { ...@@ -350,7 +355,7 @@ public class PrivacyListProvider {
*/ */
public void deletePrivacyLists(String username) { public void deletePrivacyLists(String username) {
// If there are no privacy lists stored, this method is a no-op. // If there are no privacy lists stored, this method is a no-op.
if (privacyListCount.get() == 0) { if (!databaseContainsPrivacyLists.get()) {
return; return;
} }
Connection con = null; Connection con = null;
...@@ -367,15 +372,13 @@ public class PrivacyListProvider { ...@@ -367,15 +372,13 @@ public class PrivacyListProvider {
finally { finally {
DbConnectionManager.closeConnection(pstmt, con); DbConnectionManager.closeConnection(pstmt, con);
} }
// Set the privacy list count to -1. We don't know how many privacy lists there databaseContainsPrivacyLists.set(true);
// are, but it's probably not "0", which is the case we care about.
privacyListCount.set(-1);
} }
/** /**
* Loads the total number of privacy lists stored in the database. * Loads the total number of privacy lists stored in the database to know if we must use them.
*/ */
private void loadPrivacyListCount() { private void loadDatabaseContainsPrivacyLists() {
Connection con = null; Connection con = null;
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
ResultSet rs = null; ResultSet rs = null;
...@@ -384,7 +387,7 @@ public class PrivacyListProvider { ...@@ -384,7 +387,7 @@ public class PrivacyListProvider {
pstmt = con.prepareStatement(PRIVACY_LIST_COUNT); pstmt = con.prepareStatement(PRIVACY_LIST_COUNT);
rs = pstmt.executeQuery(); rs = pstmt.executeQuery();
rs.next(); rs.next();
privacyListCount.set(rs.getInt(1)); databaseContainsPrivacyLists.set(rs.getInt(1) != 0);
} }
catch (Exception e) { catch (Exception e) {
Log.error(e.getMessage(), e); Log.error(e.getMessage(), e);
......
...@@ -20,28 +20,19 @@ ...@@ -20,28 +20,19 @@
package org.jivesoftware.util; package org.jivesoftware.util;
import org.apache.commons.lang.StringUtils;
import org.jivesoftware.database.DbConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.TimeZone;
import java.util.TimerTask;
import org.apache.commons.lang.StringUtils;
import org.jivesoftware.database.DbConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Controls Jive properties. Jive properties are only meant to be set and retrieved * Controls Jive properties. Jive properties are only meant to be set and retrieved
...@@ -565,6 +556,33 @@ public class JiveGlobals { ...@@ -565,6 +556,33 @@ public class JiveGlobals {
} }
} }
/**
* Returns an enum constant Jive property. If the specified property doesn't exist, or if it's value cannot be parsed
* as an enum constant, the <tt>defaultValue</tt> will be returned.
*
* @param name the name of the property to return.
* @param enumType the {@code Class} object of the enum type from which to return a constant.
* @param defaultValue value returned if the property doesn't exist or it's value could not be parsed.
* @param <E> The enum type whose constant is to be returned.
* @return the property value (as an enum constant) or <tt>defaultValue</tt>.
*/
public static <E extends Enum<E>> E getEnumProperty( String name, Class<E> enumType, E defaultValue )
{
String value = getProperty( name );
if ( value != null )
{
try
{
return E.valueOf( enumType, value );
}
catch ( IllegalArgumentException e )
{
// Ignore
}
}
return defaultValue;
}
/** /**
* Returns an integer value Jive property. If the specified property doesn't exist, the * Returns an integer value Jive property. If the specified property doesn't exist, the
* <tt>defaultValue</tt> will be returned. * <tt>defaultValue</tt> will be returned.
......
This diff is collapsed.
...@@ -373,8 +373,14 @@ public class ClusterExternalizableUtil implements ExternalizableUtilStrategy { ...@@ -373,8 +373,14 @@ public class ClusterExternalizableUtil implements ExternalizableUtilStrategy {
} else if (obj instanceof Date) { } else if (obj instanceof Date) {
out.writeByte(8); out.writeByte(8);
out.writeLong(((Date) obj).getTime()); out.writeLong(((Date) obj).getTime());
} else { } else if(obj instanceof byte[]){
out.writeByte(9); out.writeByte(9);
// write length
out.writeInt(((byte[]) obj).length);
// write byte array
out.write((byte[]) obj);
} else {
out.writeByte(10);
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos); ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj); oos.writeObject(obj);
...@@ -404,6 +410,10 @@ public class ClusterExternalizableUtil implements ExternalizableUtilStrategy { ...@@ -404,6 +410,10 @@ public class ClusterExternalizableUtil implements ExternalizableUtilStrategy {
} else if (type == 8) { } else if (type == 8) {
return new Date(in.readLong()); return new Date(in.readLong());
} else if (type == 9) { } else if (type == 9) {
byte[] buf = new byte[in.readInt()];
in.readFully(buf);
return buf;
} else if (type == 10) {
int len = in.readInt(); int len = in.readInt();
byte[] buf = new byte[len]; byte[] buf = new byte[len];
in.readFully(buf); in.readFully(buf);
......
...@@ -148,6 +148,11 @@ hr { ...@@ -148,6 +148,11 @@ hr {
</div> </div>
<div id="pageBody"> <div id="pageBody">
<p><b>2.6.0</b> -- September 27, 2016</p>
<ul>
<li>Added option to use XEP-0227 Compliant export and import.</li>
</ul>
<p><b>2.5.0</b> -- October 12, 2015</p> <p><b>2.5.0</b> -- October 12, 2015</p>
<ul> <ul>
<li>[<a href='http://www.igniterealtime.org/issues/browse/OF-953'>OF-953</a>] - Updated JSP libraries.</li> <li>[<a href='http://www.igniterealtime.org/issues/browse/OF-953'>OF-953</a>] - Updated JSP libraries.</li>
......
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:xml='http://www.w3.org/XML/1998/namespace'
targetNamespace='jabber:client'
xmlns='jabber:client'
elementFormDefault='qualified'>
<xs:import namespace='urn:ietf:params:xml:ns:xmpp-stanzas'
schemaLocation='http://xmpp.org/schemas/stanzaerror.xsd'/>
<xs:import namespace='http://www.w3.org/XML/1998/namespace'
schemaLocation='http://www.w3.org/2001/03/xml.xsd'/>
<xs:element name='message'>
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs='0' maxOccurs='unbounded'>
<xs:element ref='subject'/>
<xs:element ref='body'/>
<xs:element ref='thread'/>
</xs:choice>
<xs:any namespace='##other'
minOccurs='0'
maxOccurs='unbounded'
processContents='lax'/>
<xs:element ref='error'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='from'
type='xs:string'
use='optional'/>
<xs:attribute name='id'
type='xs:NMTOKEN'
use='optional'/>
<xs:attribute name='to'
type='xs:string'
use='optional'/>
<xs:attribute name='type'
use='optional'
default='normal'>
<xs:simpleType>
<xs:restriction base='xs:NMTOKEN'>
<xs:enumeration value='chat'/>
<xs:enumeration value='error'/>
<xs:enumeration value='groupchat'/>
<xs:enumeration value='headline'/>
<xs:enumeration value='normal'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='body'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:string'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='subject'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:string'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='thread'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:NMTOKEN'>
<xs:attribute name='parent'
type='xs:NMTOKEN'
use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='presence'>
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs='0' maxOccurs='unbounded'>
<xs:element ref='show'/>
<xs:element ref='status'/>
<xs:element ref='priority'/>
</xs:choice>
<xs:any namespace='##other'
minOccurs='0'
maxOccurs='unbounded'
processContents='lax'/>
<xs:element ref='error'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='from'
type='xs:string'
use='optional'/>
<xs:attribute name='id'
type='xs:NMTOKEN'
use='optional'/>
<xs:attribute name='to'
type='xs:string'
use='optional'/>
<xs:attribute name='type' use='optional'>
<xs:simpleType>
<xs:restriction base='xs:NMTOKEN'>
<xs:enumeration value='error'/>
<xs:enumeration value='probe'/>
<xs:enumeration value='subscribe'/>
<xs:enumeration value='subscribed'/>
<xs:enumeration value='unavailable'/>
<xs:enumeration value='unsubscribe'/>
<xs:enumeration value='unsubscribed'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='show'>
<xs:simpleType>
<xs:restriction base='xs:NMTOKEN'>
<xs:enumeration value='away'/>
<xs:enumeration value='chat'/>
<xs:enumeration value='dnd'/>
<xs:enumeration value='xa'/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name='status'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='string1024'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:simpleType name='string1024'>
<xs:restriction base='xs:string'>
<xs:minLength value='1'/>
<xs:maxLength value='1024'/>
</xs:restriction>
</xs:simpleType>
<xs:element name='priority' type='xs:byte'/>
<xs:element name='iq'>
<xs:complexType>
<xs:sequence>
<xs:any namespace='##other'
minOccurs='0'
maxOccurs='1'
processContents='lax'/>
<xs:element ref='error'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='from'
type='xs:string'
use='optional'/>
<xs:attribute name='id'
type='xs:NMTOKEN'
use='required'/>
<xs:attribute name='to'
type='xs:string'
use='optional'/>
<xs:attribute name='type' use='required'>
<xs:simpleType>
<xs:restriction base='xs:NMTOKEN'>
<xs:enumeration value='error'/>
<xs:enumeration value='get'/>
<xs:enumeration value='result'/>
<xs:enumeration value='set'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='error'>
<xs:complexType>
<xs:sequence xmlns:err='urn:ietf:params:xml:ns:xmpp-stanzas'>
<xs:group ref='err:stanzaErrorGroup'/>
<xs:element ref='err:text'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='by'
type='xs:string'
use='optional'/>
<xs:attribute name='type' use='required'>
<xs:simpleType>
<xs:restriction base='xs:NMTOKEN'>
<xs:enumeration value='auth'/>
<xs:enumeration value='cancel'/>
<xs:enumeration value='continue'/>
<xs:enumeration value='modify'/>
<xs:enumeration value='wait'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='jabber:iq:private'
xmlns='jabber:iq:private'
elementFormDefault='qualified'>
<xs:annotation>
<xs:documentation>
The protocol documented by this schema is defined in
XEP-0049: http://www.xmpp.org/extensions/xep-0049.html
</xs:documentation>
</xs:annotation>
<xs:element name='query'>
<xs:complexType>
<xs:sequence minOccurs='0'>
<xs:any namespace='##other'/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='jabber:iq:roster'
xmlns='jabber:iq:roster'
elementFormDefault='qualified'>
<xs:element name='query'>
<xs:complexType>
<xs:sequence>
<xs:element ref='item' minOccurs='0' maxOccurs='unbounded' />
</xs:sequence>
<xs:attribute name='ver' type='xs:string' use='optional' />
</xs:complexType>
</xs:element>
<xs:element name='item'>
<xs:complexType>
<xs:sequence>
<xs:element ref='group' minOccurs='0' maxOccurs='unbounded' />
</xs:sequence>
<xs:attribute name='approved' type='xs:boolean' use='optional' />
<xs:attribute name='ask' use='optional'>
<xs:simpleType>
<xs:restriction base='xs:NMTOKEN'>
<xs:enumeration value='subscribe' />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name='jid' type='xs:string' use='required' />
<xs:attribute name='name' type='xs:string' use='optional' />
<xs:attribute name='subscription' use='optional' default='none'>
<xs:simpleType>
<xs:restriction base='xs:NMTOKEN'>
<xs:enumeration value='both' />
<xs:enumeration value='from' />
<xs:enumeration value='none' />
<xs:enumeration value='remove' />
<xs:enumeration value='to' />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name='group' type='xs:string' />
</xs:schema>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='urn:xmpp:pie:0'
xmlns='urn:xmpp:pie:0'
elementFormDefault='qualified'>
<xs:annotation>
<xs:documentation>
The protocol documented by this schema is defined in
XEP-0227: http://xmpp.org/extensions/xep-0227.html
</xs:documentation>
</xs:annotation>
<xs:element name='server-data'>
<xs:complexType>
<xs:sequence>
<xs:element ref='host' maxOccurs='unbounded'/>
<xs:any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name='host'>
<xs:complexType>
<xs:sequence>
<xs:element ref='user' maxOccurs='unbounded'/>
<xs:any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
</xs:sequence>
<xs:attribute name='jid' type='xs:string' use='required'/>
</xs:complexType>
</xs:element>
<xs:element name='user'>
<xs:complexType>
<xs:sequence>
<xs:element ref='offline-messages' minOccurs='0' maxOccurs='1'/>
<xs:any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
</xs:sequence>
<xs:attribute name='name' type='xs:string' use='required'/>
<xs:attribute name='password' type='xs:string' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='offline-messages'>
<xs:complexType>
<xs:sequence>
<xs:any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:xml='http://www.w3.org/XML/1998/namespace'
targetNamespace='urn:ietf:params:xml:ns:xmpp-stanzas'
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'
elementFormDefault='qualified'>
<xs:import namespace='http://www.w3.org/XML/1998/namespace'
schemaLocation='http://www.w3.org/2001/03/xml.xsd'/>
<xs:element name='bad-request' type='empty'/>
<xs:element name='conflict' type='empty'/>
<xs:element name='feature-not-implemented' type='empty'/>
<xs:element name='forbidden' type='empty'/>
<xs:element name='gone' type='xs:string'/>
<xs:element name='internal-server-error' type='empty'/>
<xs:element name='item-not-found' type='empty'/>
<xs:element name='jid-malformed' type='empty'/>
<xs:element name='not-acceptable' type='empty'/>
<xs:element name='not-allowed' type='empty'/>
<xs:element name='not-authorized' type='empty'/>
<xs:element name='payment-required' type='empty'/>
<xs:element name='policy-violation' type='empty'/>
<xs:element name='recipient-unavailable' type='empty'/>
<xs:element name='redirect' type='xs:string'/>
<xs:element name='registration-required' type='empty'/>
<xs:element name='remote-server-not-found' type='empty'/>
<xs:element name='remote-server-timeout' type='empty'/>
<xs:element name='resource-constraint' type='empty'/>
<xs:element name='service-unavailable' type='empty'/>
<xs:element name='subscription-required' type='empty'/>
<xs:element name='undefined-condition' type='empty'/>
<xs:element name='unexpected-request' type='empty'/>
<xs:group name='stanzaErrorGroup'>
<xs:choice>
<xs:element ref='bad-request'/>
<xs:element ref='conflict'/>
<xs:element ref='feature-not-implemented'/>
<xs:element ref='forbidden'/>
<xs:element ref='gone'/>
<xs:element ref='internal-server-error'/>
<xs:element ref='item-not-found'/>
<xs:element ref='jid-malformed'/>
<xs:element ref='not-acceptable'/>
<xs:element ref='not-authorized'/>
<xs:element ref='not-allowed'/>
<xs:element ref='payment-required'/>
<xs:element ref='policy-violation'/>
<xs:element ref='recipient-unavailable'/>
<xs:element ref='redirect'/>
<xs:element ref='registration-required'/>
<xs:element ref='remote-server-not-found'/>
<xs:element ref='remote-server-timeout'/>
<xs:element ref='resource-constraint'/>
<xs:element ref='service-unavailable'/>
<xs:element ref='subscription-required'/>
<xs:element ref='undefined-condition'/>
<xs:element ref='unexpected-request'/>
</xs:choice>
</xs:group>
<xs:element name='text'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:string'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:simpleType name='empty'>
<xs:restriction base='xs:string'>
<xs:enumeration value=''/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) The Internet Society (2000). All Rights Reserved.
This document and translations of it may be copied and
furnished to others, and derivative works that comment
on or otherwise explain it or assist in its implmentation
may be prepared, copied, published and distributed, in whole
or in part, without restriction of any kind, provided that
the above copyright notice and this paragraph are included
on all such copies and derivative works.
However, this document itself may not be modified in any
way, such as by removing the copyright notice or references
to the Internet Society or other Internet organizations,
except as needed for the purpose of developing Internet
standards in which case the procedures for copyrights
defined in the Internet Standards process MUST be followed,
or as required to translate it into languages other than English.
The limited permissions granted above are perpetual and will
not be revoked by the Internet Society or its successors or
assigns.
This document and the information contained herein is provided
on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET
ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE
USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR
ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
PARTICULAR PURPOSE.
-->
<!-- ==== -->
<!-- NOTE: the following root element is not used in the
modified vcard-temp DTD published by the Jabber
project (now XMPP Standards Foundation) and is
included here only for historical purposes;
implementations that comply with vcard-temp must
specify the root element as vCard, not xCard. -->
<!-- Root element and container for one
or more vCard objects -->
<!ELEMENT xCard (vCard)+>
<!-- Individual vCard container -->
<!ELEMENT vCard (
(VERSION, FN, N),
(NICKNAME?,
PHOTO?,
BDAY?,
ADR?,
LABEL?,
TEL?,
EMAIL?,
JABBERID?,
MAILER?,
TZ?,
GEO?,
TITLE?,
ROLE?,
LOGO?,
AGENT?,
ORG?,
CATEGORIES?,
NOTE?,
PRODID?,
REV?,
SORT-STRING?,
SOUND?,
UID?,
URL?,
CLASS?,
KEY?,
DESC?
)*)>
<!-- vCard specification version property.
This MUST be 2.0, if the document conforms to RFC 2426. -->
<!ELEMENT VERSION (#PCDATA)>
<!-- Formatted or display name property. -->
<!ELEMENT FN (#PCDATA)>
<!-- Structured name property. Name components with multiple
values must be specified as a comma separated
list of values. -->
<!ELEMENT N ( FAMILY?, GIVEN?, MIDDLE?, PREFIX?, SUFFIX?)>
<!ELEMENT FAMILY (#PCDATA)>
<!ELEMENT GIVEN (#PCDATA)>
<!ELEMENT MIDDLE (#PCDATA)>
<!ELEMENT PREFIX (#PCDATA)>
<!ELEMENT SUFFIX (#PCDATA)>
<!-- Nickname property. Multiple nicknames must be
specified as a comma separated list value. -->
<!ELEMENT NICKNAME (#PCDATA)>
<!-- Photograph property. Value is either a BASE64 encoded
binary value or a URI to the external content. -->
<!ELEMENT PHOTO ((TYPE, BINVAL) | EXTVAL)>
<!-- Birthday property. Value must be an ISO 8601 formatted
date or date/time value. -->
<!ELEMENT BDAY (#PCDATA)>
<!-- Structured address property. Address components with
multiple values must be specified as a comma separated list
of values. -->
<!ELEMENT ADR (
HOME?,
WORK?,
POSTAL?,
PARCEL?,
(DOM | INTL)?,
PREF?,
POBOX?,
EXTADD?,
STREET?,
LOCALITY?,
REGION?,
PCODE?,
CTRY?
)>
<!ELEMENT POBOX (#PCDATA)>
<!ELEMENT EXTADD (#PCDATA)>
<!ELEMENT STREET (#PCDATA)>
<!ELEMENT LOCALITY (#PCDATA)>
<!ELEMENT REGION (#PCDATA)>
<!ELEMENT PCODE (#PCDATA)>
<!ELEMENT CTRY (#PCDATA)>
<!-- Address label property. -->
<!ELEMENT LABEL (
HOME?,
WORK?,
POSTAL?,
PARCEL?,
(DOM | INTL)?,
PREF?,
LINE+
)>
<!-- Individual label lines. -->
<!ELEMENT LINE (#PCDATA)>
<!-- Telephone number property. -->
<!ELEMENT TEL (
HOME?,
WORK?,
VOICE?,
FAX?,
PAGER?,
MSG?,
CELL?,
VIDEO?,
BBS?,
MODEM?,
ISDN?,
PCS?,
PREF?,
NUMBER
)>
<!-- Phone number value. -->
<!ELEMENT NUMBER (#PCDATA)>
<!-- Email address property. Default type is INTERNET. -->
<!ELEMENT EMAIL (
HOME?,
WORK?,
INTERNET?,
PREF?,
X400?,
USERID
)>
<!ELEMENT USERID (#PCDATA)>
<!-- NOTE: the following element was added by the Jabber
project (now XMPP Standards Foundation) to
handle Jabber IDs; the value must be in the
form of user@host -->
<!ELEMENT JABBERID (#PCDATA)>
<!-- Mailer (e.g., Mail User Agent Type) property. -->
<!ELEMENT MAILER (#PCDATA)>
<!-- Time zone's Standard Time UTC offset. Value must be an
ISO 8601 formatted UTC offset. -->
<!ELEMENT TZ (#PCDATA)>
<!-- Geographical position. Values are the decimal degress of
LATitude and LONgitude. The value should be specified to
six decimal places.-->
<!ELEMENT GEO (LAT, LON)>
<!-- Latitude value. -->
<!ELEMENT LAT (#PCDATA)>
<!-- Longitude value. -->
<!ELEMENT LON (#PCDATA)>
<!-- Title property. -->
<!ELEMENT TITLE (#PCDATA)>
<!-- Role property. -->
<!ELEMENT ROLE (#PCDATA)>
<!-- Organization logo property. -->
<!ELEMENT LOGO ((TYPE, BINVAL) | EXTVAL)>
<!-- Administrative agent property. -->
<!ELEMENT AGENT (vCard | EXTVAL)>
<!-- Organizational name and units property. -->
<!ELEMENT ORG (ORGNAME, ORGUNIT*)>
<!ELEMENT ORGNAME (#PCDATA)>
<!ELEMENT ORGUNIT (#PCDATA)>
<!-- Application specific categories property. -->
<!ELEMENT CATEGORIES (KEYWORD+)>
<!ELEMENT KEYWORD (#PCDATA)>
<!-- Commentary note property. -->
<!ELEMENT NOTE (#PCDATA)>
<!-- Identifier of product that generated the vCard property. -->
<!ELEMENT PRODID (#PCDATA)>
<!-- Last revised property. The value must be an
ISO 8601 formatted UTC date/time. -->
<!ELEMENT REV (#PCDATA)>
<!-- Sort string property. -->
<!ELEMENT SORT-STRING (#PCDATA)>
<!-- Formatted name pronunciation property. The value is
either a textual phonetic pronunciation, a BASE64
encoded binary digital audio pronunciation or a URI to
an external binary digital audio pronunciation.-->
<!ELEMENT SOUND (PHONETIC | BINVAL | EXTVAL)>
<!-- Textual phonetic pronunciation. -->
<!ELEMENT PHONETIC (#PCDATA)>
<!-- Unique identifier property. -->
<!ELEMENT UID (#PCDATA)>
<!-- Directory URL property. -->
<!ELEMENT URL (#PCDATA)>
<!-- NOTE: the following element was added by the Jabber
project (now XMPP Standards Foundation) to
handle free-form descriptive text. -->
<!ELEMENT DESC (#PCDATA)>
<!-- Privacy classification property. -->
<!ELEMENT CLASS (PUBLIC | PRIVATE | CONFIDENTIAL)>
<!ELEMENT PUBLIC EMPTY>
<!ELEMENT PRIVATE EMPTY>
<!ELEMENT CONFIDENTIAL EMPTY>
<!-- Authentication credential or encryption key property. -->
<!ELEMENT KEY (TYPE?, CRED)>
<!ELEMENT CRED (#PCDATA)>
<!-- ==== -->
<!-- Common elements. -->
<!-- Addressing type indicators. -->
<!ELEMENT HOME EMPTY>
<!ELEMENT WORK EMPTY>
<!ELEMENT POSTAL EMPTY>
<!ELEMENT PARCEL EMPTY>
<!ELEMENT DOM EMPTY>
<!ELEMENT INTL EMPTY>
<!ELEMENT PREF EMPTY>
<!ELEMENT VOICE EMPTY>
<!ELEMENT FAX EMPTY>
<!ELEMENT PAGER EMPTY>
<!ELEMENT MSG EMPTY>
<!ELEMENT CELL EMPTY>
<!ELEMENT VIDEO EMPTY>
<!ELEMENT BBS EMPTY>
<!ELEMENT MODEM EMPTY>
<!ELEMENT ISDN EMPTY>
<!ELEMENT PCS EMPTY>
<!ELEMENT INTERNET EMPTY>
<!ELEMENT X400 EMPTY>
<!-- Format type parameter. -->
<!ELEMENT TYPE (#PCDATA)>
<!-- Base64 encoded binary value. -->
<!ELEMENT BINVAL (#PCDATA)>
<!-- URI to external binary value -->
<!ELEMENT EXTVAL (#PCDATA)>
<!-- ==== -->
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3c.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="Openfire"> <xs:element name="Openfire">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
<xs:element name="User"> <xs:element name="User">
<xs:complexType> <xs:complexType>
<xs:all> <xs:all>
<xs:element ref="Username" use="required" /> <xs:element ref="Username" />
<xs:element ref="Password" use="required" /> <xs:element ref="Password" />
<xs:element ref="Email" /> <xs:element ref="Email" />
<xs:element ref="Name" /> <xs:element ref="Name" />
<xs:element ref="CreationDate" /> <xs:element ref="CreationDate" />
......
<?xml version='1.0'?>
<!-- <!DOCTYPE xs:schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" > -->
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" xmlns:xs="http://www.w3.org/2001/XMLSchema" xml:lang="en">
<xs:annotation>
<xs:documentation>
See http://www.w3.org/XML/1998/namespace.html and
http://www.w3.org/TR/REC-xml for information about this namespace.
This schema document describes the XML namespace, in a form
suitable for import by other schema documents.
Note that local names in this namespace are intended to be defined
only by the World Wide Web Consortium or its subgroups. The
following names are currently defined in this namespace and should
not be used with conflicting semantics by any Working Group,
specification, or document instance:
base (as an attribute name): denotes an attribute whose value
provides a URI to be used as the base for interpreting any
relative URIs in the scope of the element on which it
appears; its value is inherited. This name is reserved
by virtue of its definition in the XML Base specification.
lang (as an attribute name): denotes an attribute whose value
is a language code for the natural language of the content of
any element; its value is inherited. This name is reserved
by virtue of its definition in the XML specification.
space (as an attribute name): denotes an attribute whose
value is a keyword indicating what whitespace processing
discipline is intended for the content of the element; its
value is inherited. This name is reserved by virtue of its
definition in the XML specification.
Father (in any context at all): denotes Jon Bosak, the chair of
the original XML Working Group. This name is reserved by
the following decision of the W3C XML Plenary and
XML Coordination groups:
In appreciation for his vision, leadership and dedication
the W3C XML Plenary on this 10th day of February, 2000
reserves for Jon Bosak in perpetuity the XML name
xml:Father
</xs:documentation>
</xs:annotation>
<xs:annotation>
<xs:documentation>This schema defines attributes and an attribute group
suitable for use by
schemas wishing to allow xml:base, xml:lang or xml:space attributes
on elements they define.
To enable this, such a schema must import this schema
for the XML namespace, e.g. as follows:
&lt;schema . . .>
. . .
&lt;import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/03/xml.xsd"/>
Subsequently, qualified reference to any of the attributes
or the group defined below will have the desired effect, e.g.
&lt;type . . .>
. . .
&lt;attributeGroup ref="xml:specialAttrs"/>
will define a type which will schema-validate an instance
element with any of those attributes</xs:documentation>
</xs:annotation>
<xs:annotation>
<xs:documentation>In keeping with the XML Schema WG's standard versioning
policy, this schema document will persist at
http://www.w3.org/2001/03/xml.xsd.
At the date of issue it can also be found at
http://www.w3.org/2001/xml.xsd.
The schema document at that URI may however change in the future,
in order to remain compatible with the latest version of XML Schema
itself. In other words, if the XML Schema namespace changes, the version
of this document at
http://www.w3.org/2001/xml.xsd will change
accordingly; the version at
http://www.w3.org/2001/03/xml.xsd will not change.
</xs:documentation>
</xs:annotation>
<xs:attribute name="lang" type="xs:language">
<xs:annotation>
<xs:documentation>In due course, we should install the relevant ISO 2- and 3-letter
codes as the enumerated possible values . . .</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="space" default="preserve">
<xs:simpleType>
<xs:restriction base="xs:NCName">
<xs:enumeration value="default"/>
<xs:enumeration value="preserve"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="base" type="xs:anyURI">
<xs:annotation>
<xs:documentation>See http://www.w3.org/TR/xmlbase/ for
information about this attribute.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attributeGroup name="specialAttrs">
<xs:attribute ref="xml:base"/>
<xs:attribute ref="xml:lang"/>
<xs:attribute ref="xml:space"/>
</xs:attributeGroup>
</xs:schema>
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
<name>User Import Export</name> <name>User Import Export</name>
<description>Enables import and export of user data</description> <description>Enables import and export of user data</description>
<author>Ryan Graham</author> <author>Ryan Graham</author>
<version>2.5.0</version> <version>2.6.0</version>
<date>10/12/2015</date> <date>09/26/2016</date>
<minServerVersion>4.0.0</minServerVersion> <minServerVersion>4.0.0</minServerVersion>
<adminconsole> <adminconsole>
......
...@@ -183,13 +183,14 @@ userImportExport.jar file over the existing file.</p> ...@@ -183,13 +183,14 @@ userImportExport.jar file over the existing file.</p>
<p>Presently, there is nothing that can be configured for the user import/export plugin.</p> <p>Presently, there is nothing that can be configured for the user import/export plugin.</p>
<h2>Using the Plugin</h2> <h2>Using the Plugin</h2>
<p>The plugin is accessed via the "User Import & Export" sidebar item located under the <p>The plugin is accessed via the "User Import &amp; Export" sidebar item located under the
"Users/Groups" tab in the Admin Console. Note: if you are using a read-only user store such as LDAP "Users/Groups" tab in the Admin Console. Note: if you are using a read-only user store such as LDAP
or POP3 this plugin will still work with two caveats: or POP3 this plugin will still work with two caveats:
<ol> <ol>
<li>When exporting, the username will be placed in the password element. <li>When exporting, the username will be placed in the password element.
<li>When importing, no new users will be created but if the user exists in the user store thier roster will be loaded. <li>When importing, no new users will be created but if the user exists in the user store the roster will be loaded.
</ol> </ol>
<li><strong>Importing</strong> - Select the "Import User Data" option from the user import/export selection <li><strong>Importing</strong> - Select the "Import User Data" option from the user import/export selection
page. On the import page, use the "Browse" button to locate the file that contains the user page. On the import page, use the "Browse" button to locate the file that contains the user
information you want to locate and then click on the "Import" button. If the plugin is successful information you want to locate and then click on the "Import" button. If the plugin is successful
...@@ -198,7 +199,7 @@ If the plugin was not successful in importing all user data you, will receive a ...@@ -198,7 +199,7 @@ If the plugin was not successful in importing all user data you, will receive a
what might have gone wrong. If during the import process, the plugin detects that you are trying to what might have gone wrong. If during the import process, the plugin detects that you are trying to
import a user that already exists in the system, it will not import that user or any roster import a user that already exists in the system, it will not import that user or any roster
information, except in the case of using a read-only user store.</li> information, except in the case of using a read-only user store.</li>
<br> <br/>
<li><strong>Exporting</strong> - Select the "Export User Data" option from the user import/export selection <li><strong>Exporting</strong> - Select the "Export User Data" option from the user import/export selection
page. User data can be exported either to a file or directly to the screen. To export to a file, page. User data can be exported either to a file or directly to the screen. To export to a file,
select the "To File" radio button, enter the name you want your export file to be called in the select the "To File" radio button, enter the name you want your export file to be called in the
...@@ -206,9 +207,9 @@ select the "To File" radio button, enter the name you want your export file to b ...@@ -206,9 +207,9 @@ select the "To File" radio button, enter the name you want your export file to b
an ".xml" extension to the file name if it is not already present. To export to the screen, select an ".xml" extension to the file name if it is not already present. To export to the screen, select
the "To Screen" radio button and then click on the "Export" button. The user data will be placed in the "To Screen" radio button and then click on the "Export" button. The user data will be placed in
the provided text area.</li> the provided text area.</li>
<br> <br/>
<li><strong>Migration</strong> - To import user data from another instant messaging system using the plugin, <li><strong>Migration</strong> - To import user data from another instant messaging system using the plugin,
the import file must conform to the openfire-user-schema.xsd.xml schema file (located in the classes the import file must conform to the wildfire-user-schema.xsd.xml schema file (located in the classes
directory of the userImportExport.jar). When importing a user data file the plugin will first validate directory of the userImportExport.jar). When importing a user data file the plugin will first validate
the file against the schema file. If the plugin cannot validate the import file the user data will the file against the schema file. If the plugin cannot validate the import file the user data will
not be imported. During the import process the plugin gives you the ability to update user roster not be imported. During the import process the plugin gives you the ability to update user roster
...@@ -406,6 +407,15 @@ Below is a list of the different status types and what their associated numbers ...@@ -406,6 +407,15 @@ Below is a list of the different status types and what their associated numbers
</table> </table>
</div> </div>
<p>
<li><strong>XEP-0227 Compliance</strong> - Both Import and Export have an option to
enable to use the format as defined in <a href="http://www.xmpp.org/extensions/xep-0227.html"
target="_blank">XEP-0227</a>. This format is intended as a Portable Import/Export Format for
XMPP-IM Servers. When importing, the xml input file will be checked against a set of xsd's
which are compliant with XEPP-0227. These are located in the classes directory of the userImportExport.jar.
</li>
</p>
</div> </div>
</div> </div>
......
/**
*
* Copyright 2016 Anno van Vliet
*
* Licensed 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.jivesoftware.openfire.plugin;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
/**
* An interface to an In and Exporter.
*
* @author Anno van Vliet
*
*/
public interface InExporter {
/**
* Export the users in a Document
* @param userManager
*
* @return
*/
Document exportUsers();
/**
* Validate the xml to the correct XSD.
* @param doc
*
* @return is Valid xml
* @throws IOException
* @throws DocumentException
*/
boolean validate(InputStream doc);
/**
* Import users
*
* @param inputStream
* @param previousDomain
* @param isUserProviderReadOnly
* @return
*/
List<String> importUsers(InputStream inputStream, String previousDomain, boolean isUserProviderReadOnly);
}
/**
*
* Copyright 2016 Anno van Vliet
*
* Licensed 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.jivesoftware.openfire.plugin;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.DOMReader;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.AuthFactory;
import org.jivesoftware.openfire.roster.RosterItem;
import org.jivesoftware.openfire.roster.RosterItemProvider;
import org.jivesoftware.openfire.roster.RosterManager;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
import gnu.inet.encoding.Stringprep;
import gnu.inet.encoding.StringprepException;
/**
* Export and import Users in the Openfire XML format.
*
* @author Anno van Vliet
*
*/
public class OpenfireExporter implements InExporter {
private static final Logger Log = LoggerFactory.getLogger(OpenfireExporter.class);
private final String serverName;
private final UserManager userManager;
private final RosterItemProvider rosterItemProvider;
/**
*
*/
public OpenfireExporter() {
serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
userManager = UserManager.getInstance();
rosterItemProvider = RosterManager.getRosterItemProvider();
}
/**
* @param serverName
* @param userManager
* @param rosterItemProvider
*/
public OpenfireExporter(String serverName, UserManager userManager, RosterItemProvider rosterItemProvider) {
super();
this.serverName = serverName;
this.userManager = userManager;
this.rosterItemProvider = rosterItemProvider;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.plugin.Exporter#exportUsers()
*/
@Override
public Document exportUsers() {
Log.debug("exportUsers");
Document document = DocumentHelper.createDocument();
Element root = document.addElement("Openfire");
Collection<User> users = userManager.getUsers();
for (User user : users) {
Element userElement = root.addElement("User");
String userName = user.getUsername();
userElement.addElement("Username").addText(userName);
try {
String pw = AuthFactory.getPassword(user.getUsername());
userElement.addElement("Password").addText(pw);
}
catch (UserNotFoundException e) {
Log.info("User " + userName + " not found, setting their password to their username");
userElement.addElement("Password").addText(userName);
}
catch (UnsupportedOperationException e) {
Log.info("Unable to retrieve " + userName + " password, setting their password to their username");
userElement.addElement("Password").addText(userName);
}
userElement.addElement("Email").addText(user.getEmail() == null ? "" : user.getEmail());
String name = user.getName();
userElement.addElement("Name").addText(name == null ? "" : name);
//creation and modified date are not used as part of the import process but are exported
//for historical purposes, should they be formatted differently?
userElement.addElement("CreationDate").addText(String.valueOf(user.getCreationDate().getTime()));
userElement.addElement("ModifiedDate").addText(String.valueOf(user.getModificationDate().getTime()));
Element rosterElement = userElement.addElement("Roster");
Collection<RosterItem> roster = user.getRoster().getRosterItems();
for (RosterItem ri : roster) {
Element itemElement = rosterElement.addElement("Item");
itemElement.addAttribute("jid", ri.getJid().toBareJID());
itemElement.addAttribute("askstatus", String.valueOf(ri.getAskStatus().getValue()));
itemElement.addAttribute("recvstatus", String.valueOf(ri.getRecvStatus().getValue()));
itemElement.addAttribute("substatus", String.valueOf(ri.getSubStatus().getValue()));
itemElement.addAttribute("name", ri.getNickname());
List<String> groups = ri.getGroups();
for (String group : groups) {
if (group != null && group.trim().length() > 0) {
itemElement.addElement("Group").addText(group);
}
}
}
}
return document;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.plugin.InExporter#validate()
*/
@Override
public boolean validate(InputStream file) {
Log.debug("validate");
org.w3c.dom.Document doc = new UserSchemaValidator(file, "wildfire-user-schema.xsd.xml").validateAndParse();
return ( doc != null);
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.plugin.InExporter#importUsers(java.io.InputStream, java.lang.String, boolean)
*/
@Override
public List<String> importUsers(InputStream inputStream, String previousDomain, boolean isUserProviderReadOnly) {
Log.debug("importUsers");
DOMReader xmlReader = new DOMReader();
Document doc = xmlReader.read(new UserSchemaValidator(inputStream).validateAndParse());
return importUsers(doc, previousDomain, isUserProviderReadOnly);
}
/*
* (non-Javadoc)
*
* @see
* org.jivesoftware.openfire.plugin.InExporter#importUsers(org.dom4j.Document,
* java.lang.String)
*/
@SuppressWarnings("unchecked")
private List<String> importUsers(Document document, String previousDomain, boolean isUserProviderReadOnly) {
Log.debug("importUsers");
List<String> invalidUsers = new ArrayList<String>();
Element users = document.getRootElement();
Iterator<Element> usersIter = users.elementIterator("User");
while (usersIter.hasNext()) {
Element user = usersIter.next();
String userName = null;
String password = null;
String email = null;
String name = null;
List<RosterItem> rosterItems = new ArrayList<RosterItem>();
Iterator<Element> userElements = user.elementIterator();
while (userElements.hasNext()) {
Element userElement = userElements.next();
String nameElement = userElement.getName();
if ("Username".equals(nameElement)) {
userName = userElement.getText();
} else if ("Password".equals(nameElement)) {
password = userElement.getText();
} else if ("Name".equals(nameElement)) {
name = userElement.getText();
} else if ("Email".equals(nameElement)) {
email = userElement.getText();
} else if ("Roster".equals(nameElement)) {
Iterator<Element> rosterIter = userElement.elementIterator("Item");
while (rosterIter.hasNext()) {
Element rosterElement = rosterIter.next();
String jid = rosterElement.attributeValue("jid");
String askstatus = rosterElement.attributeValue("askstatus");
String recvstatus = rosterElement.attributeValue("recvstatus");
String substatus = rosterElement.attributeValue("substatus");
String nickname = rosterElement.attributeValue("name");
List<String> groups = new ArrayList<String>();
Iterator<Element> groupIter = rosterElement.elementIterator("Group");
while (groupIter.hasNext()) {
Element group = groupIter.next();
String groupName = group.getText();
if (groupName != null && groupName.trim().length() > 0) {
groups.add(groupName);
}
}
// used for migration
if (previousDomain != null) {
jid = jid.replace(previousDomain, serverName);
}
rosterItems.add(new RosterItem(new JID(jid), RosterItem.SubType.getTypeFromInt(Integer.parseInt(substatus)),
RosterItem.AskType.getTypeFromInt(Integer.parseInt(askstatus)), RosterItem.RecvType.getTypeFromInt(Integer.parseInt(recvstatus)),
nickname, groups));
}
}
}
if ((userName != null) && (password != null)) {
try {
userName = Stringprep.nodeprep(userName);
if (isUserProviderReadOnly) {
userManager.createUser(userName, password, name, email);
}
// Check to see user exists before adding their roster, this is for
// read-only user providers.
userManager.getUser(userName);
for (RosterItem ri : rosterItems) {
rosterItemProvider.createItem(userName, ri);
}
} catch (StringprepException se) {
Log.info("Invalid username " + userName);
invalidUsers.add(userName);
} catch (UserAlreadyExistsException e) {
Log.info("User already exists " + userName);
invalidUsers.add(userName);
} catch (UserNotFoundException e) {
Log.info("User not found " + userName);
invalidUsers.add(userName);
}
}
}
return invalidUsers;
}
}
/**
*
* Copyright 2016 Anno van Vliet
*
* Licensed 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.jivesoftware.openfire.plugin; package org.jivesoftware.openfire.plugin;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.SAXParserFactory; import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.apache.commons.fileupload.FileItem;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.dom4j.io.SAXWriter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xml.sax.ContentHandler; import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler; import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator; import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException; import org.xml.sax.SAXParseException;
import com.sun.msv.reader.util.GrammarLoader; /**
import com.sun.msv.reader.util.IgnoreController; * An utility to convert a file to XML, and validate the XML against a set of XML Schema files.
import com.sun.msv.verifier.DocumentDeclaration; *
import com.sun.msv.verifier.Verifier; * @author Anno van Vliet
*
*/
public class UserSchemaValidator { public class UserSchemaValidator {
private static final Logger Log = LoggerFactory.getLogger(UserSchemaValidator.class); private static final Logger Log = LoggerFactory.getLogger(UserSchemaValidator.class);
private Document doc; private static final CharSequence STRICT_DECLARATION_MSG = "The matching wildcard is strict, but no declaration can be found for element";
private String schema;
private final InputStream source;
private final Source[] schemaSources;
/**
* Construct a Validator object which parses and validates a input source.
*
* @param source XML input document
* @param schemaFile zero or more schema files.
*/
UserSchemaValidator(InputStream source, String... schemaFile) {
this.source = source;
List<Source> sourceList = new ArrayList<Source>();
for (String schema : schemaFile) {
try {
URL schemaURL = this.getClass().getClassLoader().getResource(schema);
if (schemaURL != null) {
sourceList.add(new StreamSource(schemaURL.openStream()));
} else {
Log.warn("Cannot find schema definition " + schema);
}
} catch (IOException e) {
Log.warn("Cannot open schema definition " + schema + " : " + e.getMessage());
Log.debug("", e);
}
}
UserSchemaValidator(FileItem usersFile, String schemaFile) throws DocumentException, IOException { schemaSources = new Source[sourceList.size()];
SAXReader reader = new SAXReader(); sourceList.toArray(schemaSources);
doc = reader.read(usersFile.getInputStream());
}
/**
* Perform a validate and a parse of the specified source document.
* Validating is done with the specified schemafiles.
*
* @return A Document when the validation s successful.
*/
Document validateAndParse() {
ValidatorErrorHandler handler = new ValidatorErrorHandler();
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setXIncludeAware(true);
documentBuilderFactory.setValidating(false);
// We don't want xml:base and xml:lang attributes in the output.
documentBuilderFactory.setFeature(
"http://apache.org/xml/features/xinclude/fixup-base-uris", false);
documentBuilderFactory.setFeature(
"http://apache.org/xml/features/xinclude/fixup-language", false);
if ( schemaSources.length > 0 ) {
Log.info("Checking Schema's");
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setErrorHandler(handler);
URL schemaURL = this.getClass().getClassLoader().getResource(schemaFile); Schema schema = schemaFactory.newSchema(schemaSources);
schema = schemaURL.toExternalForm();
} documentBuilderFactory.setSchema(schema);
Log.info("Start validating document");
boolean validate() { } else {
try { Log.info("Loading document");
SAXParserFactory saxFactory = SAXParserFactory.newInstance(); }
saxFactory.setNamespaceAware(true);
DocumentDeclaration docDeclaration = GrammarLoader.loadVGM(schema, new IgnoreController() { DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
@Override
public void error(Locator[] locations, handler.reset();
String message,
Exception exception) { // Communicate some info about the Xincludes in the imported file. These imports should be resolvable by the server.
Log.error("ERROR: " + message); documentBuilder.setEntityResolver( new EntityResolver() {
}
public void error(Locator[] locations, String message) {
Log.error("WARNING: " + message);
}
}, saxFactory);
ValidatorErrorHandler validatorErrorHandler = new ValidatorErrorHandler(); @Override
Verifier verifier = new Verifier(docDeclaration, validatorErrorHandler); public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
Log.info(String.format("resolved Entity:%s %s", (publicId != null ? publicId : "") , systemId));
SAXWriter writer = new SAXWriter((ContentHandler) verifier); return null;
writer.setErrorHandler(validatorErrorHandler);
writer.write(doc);
if (verifier.isValid()) {
return true;
} else {
Log.error(doc.getName() + " is invalid.");
return false;
}
} catch (Exception e) {
Log.error(e.getMessage(), e);
return false;
} }
} );
documentBuilder.setErrorHandler(handler);
Document result = documentBuilder.parse(source);
if (result != null && handler.isValid()) {
return result;
} else {
Log.warn(String.format("document is invalid. %1$d errors found.", handler.getNrOfErrors()));
return null;
}
} catch (Exception e) {
Log.warn(String.format("document validation failed. %1$d errors found.", handler.getNrOfErrors()));
Log.debug("", e);
return null;
} }
}
private class ValidatorErrorHandler implements ErrorHandler {
public void error(SAXParseException e) {
Log.error("ERROR:" + e);
}
public void fatalError(SAXParseException e) { private class ValidatorErrorHandler implements ErrorHandler {
Log.error("Fatal:" + e); private int nrOfErrors = 0;
}
public void warning(SAXParseException e) { public void error(SAXParseException e) {
Log.error("Warning:" + e);
} if (e.getMessage().contains(STRICT_DECLARATION_MSG)) {
Log.warn("This error indicates there is no XML Schema to validate the refered element: " + e.getLocalizedMessage());
} else {
Log.warn("ERROR:" + e.getLocalizedMessage());
nrOfErrors++;
}
}
public void fatalError(SAXParseException e) {
Log.error("Fatal:" + e.getLocalizedMessage());
nrOfErrors++;
} }
public void warning(SAXParseException e) {
Log.error("Warning:" + e.getLocalizedMessage());
}
public void reset() {
nrOfErrors = 0;
}
public boolean isValid() {
return nrOfErrors == 0;
}
/**
* @return the nrOfErrors
*/
public int getNrOfErrors() {
return nrOfErrors;
}
}
} }
/**
*
* Copyright 2016 Anno van Vliet
*
* Licensed 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.jivesoftware.openfire.plugin;
/**
* Factory to instantiate the correct InExporter.
*
* @author Anno van Vliet
*
*/
public class XMLImportExportFactory {
/**
* @param xep227Support
* @return
*/
public static InExporter getExportInstance(boolean xep227Support) {
if ( xep227Support ) {
return new Xep227Exporter();
} else {
return new OpenfireExporter();
}
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy.MM.dd HH:mm:ss} %-5p [%t]: %c - %m%n" />
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="debug" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
<!-- OF-1095: Uniform output of loading/unloading of plugins to std-out. -->
<logger name="org.jivesoftware.openfire.container.PluginManager">
<appender-ref ref="console"/>
</logger>
<logger name="org.jivesoftware.openfire.container.PluginMonitor">
<appender-ref ref="console"/>
</logger>
<!-- OF-506: Jetty INFO messages are generally not useful. Ignore them by default. -->
<logger name="org.eclipse.jetty">
<level value="warn" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="console" />
</root>
</log4j:configuration>
/**
*
* Copyright 2016 Anno van Vliet
*
* Licensed 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.jivesoftware.openfire.plugin;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.logging.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.jivesoftware.openfire.roster.DefaultRosterItemProvider;
import org.jivesoftware.openfire.roster.RosterItem;
import org.jivesoftware.openfire.roster.RosterItemProvider;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.util.JiveGlobals;
import org.junit.Before;
import org.junit.Test;
/**
* Test Openfire flavour of Import and export
*
* @author Anno van Vliet
*
*/
public class OpenfireExporterTest {
public class TestRosterItemProvider extends DefaultRosterItemProvider implements RosterItemProvider {
/* (non-Javadoc)
* @see org.jivesoftware.openfire.roster.DefaultRosterItemProvider#createItem(java.lang.String, org.jivesoftware.openfire.roster.RosterItem)
*/
@Override
public RosterItem createItem(String username, RosterItem item) throws UserAlreadyExistsException {
logger.finest("createItem:" + username + " - " + item);
return item;
}
}
private static Logger logger = Logger.getLogger(OpenfireExporterTest.class.getName());
private UserManager userManager;
private RosterItemProvider rosterItemProvider;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
URL url = this.getClass().getResource("test-openfire.xml");
File f = new File(url.getFile());
JiveGlobals.setConfigName(f.getName());
JiveGlobals.setHomeDirectory(f.getParent());
JiveGlobals.setProperty("provider.user.className",
"org.jivesoftware.openfire.plugin.TestUserProvider");
userManager = UserManager.getInstance();
rosterItemProvider = new TestRosterItemProvider();
}
/**
* Test method for {@link org.jivesoftware.openfire.plugin.OpenfireExporter#exportUsers(org.jivesoftware.openfire.user.UserManager)}.
* @throws UserAlreadyExistsException
* @throws IOException
*/
@Test
public void testExportUsers() throws UserAlreadyExistsException, IOException {
InExporter testobject = new OpenfireExporter("serverName",userManager,rosterItemProvider);
for (int i = 0; i < 10; i++) {
userManager.createUser("username" + i,"pw" , "name" + i, "email" + i);
}
Document result = testobject.exportUsers();
assertNotNull(result);
assertEquals(1, result.nodeCount());
assertNotNull(result.node(0));
Element elem = ((Element)result.node(0));
assertEquals(10, elem.nodeCount());
assertNotNull(elem.node(0));
assertEquals(7, ((Element)elem.node(0)).nodeCount());
ByteArrayOutputStream out = new ByteArrayOutputStream();
XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
writer.write(result);
logger.fine(out.toString() );
assertNotNull(testobject.validate(new ByteArrayInputStream(out.toByteArray())));
}
/**
* @throws IOException
* @throws DocumentException
*
*/
@Test
public void testImportUser() throws DocumentException, IOException {
logger.finest("testImportUser");
InExporter testobject = new OpenfireExporter("serverName",userManager,rosterItemProvider);
String TEST_IMPORT_FILE = "test-openfire-import.xml";
InputStream stream = this.getClass().getResourceAsStream(TEST_IMPORT_FILE);
assertTrue("Invalid input", testobject.validate(stream));
stream.close();
String previousDomain = null;
boolean isUserProviderReadOnly = false;
stream = this.getClass().getResourceAsStream(TEST_IMPORT_FILE);
List<String> res = testobject.importUsers(stream, previousDomain, isUserProviderReadOnly);
assertNotNull(res);
assertEquals(0, res.size());
stream.close();
}
}
/**
*
* Copyright 2016 Anno van Vliet
*
* Licensed 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.jivesoftware.openfire.plugin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Logger;
import org.jivesoftware.openfire.roster.Roster;
import org.jivesoftware.openfire.roster.RosterItem;
import org.jivesoftware.openfire.roster.RosterItem.AskType;
import org.jivesoftware.openfire.roster.RosterItem.RecvType;
import org.jivesoftware.openfire.roster.RosterItem.SubType;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.user.UserProvider;
import org.xmpp.packet.JID;
/**
* A Dummy implementation for the UserProvider for testing purposes.
*
* @author Anno van Vliet
*
*/
public class TestUserProvider implements UserProvider {
public class TestRoster extends Roster {
private final Collection<RosterItem> rosteritems;
/**
*
*/
public TestRoster() {
rosteritems = new ArrayList<RosterItem>();
JID jid = new JID("roster@jid.test");
SubType subStatus = SubType.BOTH;
AskType askStatus = AskType.NONE;
RecvType recvStatus = RecvType.SUBSCRIBE;
String nickname = "nick";
List<String> groups = null;
rosteritems.add(new RosterItem(1, jid, subStatus, askStatus, recvStatus, nickname, groups));
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.roster.Roster#getRosterItems()
*/
@Override
public Collection<RosterItem> getRosterItems() {
logger.finest("getRosterItems");
return rosteritems;
}
}
public class TestUser extends User {
private final Roster roster;
/**
* @param username
* @param name
* @param email
* @param creationDate
* @param modificationDate
*/
public TestUser(String username, String name, String email, Date creationDate, Date modificationDate) {
super(username, name, email, creationDate, modificationDate);
roster = new TestRoster();
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.User#getRoster()
*/
@Override
public Roster getRoster() {
logger.finest("getRoster");
return roster;
}
}
private static Logger logger = Logger.getLogger(TestUserProvider.class.getName());
private final Map<String,User> userList;
/**
*
*/
public TestUserProvider() {
userList = new TreeMap<String,User>();
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#loadUser(java.lang.String)
*/
@Override
public User loadUser(String username) throws UserNotFoundException {
logger.finest("loadUser");
return userList.get(username);
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#createUser(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public User createUser(String username, String password, String name, String email) throws UserAlreadyExistsException {
logger.finest("createUser");
Date creationDate = new Date();
Date modificationDate = new Date();
User u = new TestUser(username, name, email, creationDate, modificationDate);
userList.put(username, u);
return u;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#deleteUser(java.lang.String)
*/
@Override
public void deleteUser(String username) {
logger.finest("deleteUser");
userList.remove(username);
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#getUserCount()
*/
@Override
public int getUserCount() {
logger.finest("getUserCount");
return userList.size();
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#getUsers()
*/
@Override
public Collection<User> getUsers() {
logger.finest("getUsers");
return userList.values();
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#getUsernames()
*/
@Override
public Collection<String> getUsernames() {
logger.finest("getUsernames");
return userList.keySet();
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#getUsers(int, int)
*/
@Override
public Collection<User> getUsers(int startIndex, int numResults) {
logger.finest("getUsers");
return null;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#setName(java.lang.String, java.lang.String)
*/
@Override
public void setName(String username, String name) throws UserNotFoundException {
logger.finest("setName");
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#setEmail(java.lang.String, java.lang.String)
*/
@Override
public void setEmail(String username, String email) throws UserNotFoundException {
logger.finest("setEmail");
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#setCreationDate(java.lang.String, java.util.Date)
*/
@Override
public void setCreationDate(String username, Date creationDate) throws UserNotFoundException {
logger.finest("setCreationDate");
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#setModificationDate(java.lang.String, java.util.Date)
*/
@Override
public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException {
logger.finest("setModificationDate");
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#getSearchFields()
*/
@Override
public Set<String> getSearchFields() throws UnsupportedOperationException {
logger.finest("getSearchFields");
return null;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#findUsers(java.util.Set, java.lang.String)
*/
@Override
public Collection<User> findUsers(Set<String> fields, String query) throws UnsupportedOperationException {
logger.finest("findUsers");
return null;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#findUsers(java.util.Set, java.lang.String, int, int)
*/
@Override
public Collection<User> findUsers(Set<String> fields, String query, int startIndex, int numResults) throws UnsupportedOperationException {
logger.finest("findUsers");
return null;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#isReadOnly()
*/
@Override
public boolean isReadOnly() {
logger.finest("isReadOnly");
return false;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#isNameRequired()
*/
@Override
public boolean isNameRequired() {
logger.finest("isNameRequired");
return false;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.user.UserProvider#isEmailRequired()
*/
@Override
public boolean isEmailRequired() {
logger.finest("isEmailRequired");
return false;
}
}
/**
*
* Copyright 2016 Anno van Vliet
*
* Licensed 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.jivesoftware.openfire.plugin;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.dom.DOMElement;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.jivesoftware.openfire.OfflineMessage;
import org.jivesoftware.openfire.OfflineMessageStore;
import org.jivesoftware.openfire.PrivateStorage;
import org.jivesoftware.openfire.roster.DefaultRosterItemProvider;
import org.jivesoftware.openfire.roster.RosterItem;
import org.jivesoftware.openfire.roster.RosterItemProvider;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.vcard.VCardManager;
import org.jivesoftware.util.JiveGlobals;
import org.junit.Before;
import org.junit.Test;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
/**
* XEPP-0227 compliant import and export.
*
* @author Anno van Vliet
*
*/
public class Xep227ExporterTest {
public class TestVCardManager extends VCardManager {
/* (non-Javadoc)
* @see org.jivesoftware.openfire.vcard.VCardManager#getVCard(java.lang.String)
*/
@Override
public Element getVCard(String username) {
logger.finest("getVCard");
return new DOMElement("VCARD");
}
}
public class TestOfflineMessageStore extends OfflineMessageStore {
//private Collection<OfflineMessage> offMsgs = Collections.emptyList();
/* (non-Javadoc)
* @see org.jivesoftware.openfire.OfflineMessageStore#getMessages(java.lang.String, boolean)
*/
@Override
public Collection<OfflineMessage> getMessages(String username, boolean delete) {
logger.finest("getMessages");
Collection<OfflineMessage> offMsgs = new ArrayList<OfflineMessage>();
Element element = new DOMElement("message");
Date creationDate = new Date();
OfflineMessage offmsg = new OfflineMessage(creationDate, element);
offmsg.setFrom(new JID("of@server.id"));
offmsg.setTo(new JID("of@server.id"));
offmsg.setBody("text");
offMsgs.add(offmsg);
return offMsgs;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.OfflineMessageStore#addMessage(org.xmpp.packet.Message)
*/
@Override
public void addMessage(Message message) {
logger.finest("addMessage:" + message);
assertNotNull(message);
}
}
public class TestRosterItemProvider extends DefaultRosterItemProvider implements RosterItemProvider {
/* (non-Javadoc)
* @see org.jivesoftware.openfire.roster.DefaultRosterItemProvider#createItem(java.lang.String, org.jivesoftware.openfire.roster.RosterItem)
*/
@Override
public RosterItem createItem(String username, RosterItem item) throws UserAlreadyExistsException {
logger.finest("createItem:" + username + " - " + item);
return item;
}
}
private static Logger logger = Logger.getLogger(Xep227ExporterTest.class.getName());
private OfflineMessageStore offlineMessagesStore;
private VCardManager vCardManager;
private PrivateStorage privateStorage;
private UserManager userManager;
private RosterItemProvider rosterItemProvider;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
URL url = this.getClass().getResource("test-openfire.xml");
File f = new File(url.getFile());
JiveGlobals.setConfigName(f.getName());
JiveGlobals.setHomeDirectory(f.getParent());
JiveGlobals.setProperty("provider.user.className",
"org.jivesoftware.openfire.plugin.TestUserProvider");
offlineMessagesStore = new TestOfflineMessageStore();
vCardManager = new TestVCardManager();
privateStorage = null;
userManager = UserManager.getInstance();
rosterItemProvider = new TestRosterItemProvider();
//Empty users
List<User> l = new ArrayList<>(userManager.getUsers());
for (User user : l ) {
userManager.deleteUser(user);
}
}
/**
* Test method for {@link org.jivesoftware.openfire.plugin.OpenfireExporter#exportUsers(org.jivesoftware.openfire.user.UserManager)}.
* @throws UserAlreadyExistsException
* @throws IOException
*/
@Test
public void testExportUsers() throws UserAlreadyExistsException, IOException {
InExporter testobject = new Xep227Exporter("serverName", offlineMessagesStore, vCardManager, privateStorage, userManager, null);
for (int i = 0; i < 10; i++) {
userManager.createUser("username" + i,"pw" , "name" + i, "email" + i);
}
Document result = testobject.exportUsers();
assertNotNull(result);
assertEquals(1, result.nodeCount());
assertNotNull(result.node(0));
Element elem = ((Element)result.node(0));
assertEquals(1, elem.nodeCount());
assertNotNull(elem.node(0));
elem = ((Element)elem.node(0));
assertEquals(10, elem.nodeCount());
assertNotNull(elem.node(0));
elem = ((Element)elem.node(0));
assertEquals(3, elem.nodeCount());
assertEquals(2, elem.attributeCount());
ByteArrayOutputStream out = new ByteArrayOutputStream();
XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
writer.write(result);
logger.fine(out.toString() );
assertTrue("Invalid input", testobject.validate(new ByteArrayInputStream(out.toByteArray())));
}
/**
* @throws IOException
* @throws DocumentException
*
*/
@Test
public void testValidateUser() throws DocumentException, IOException {
logger.finest("testImportUser");
InExporter testobject = new Xep227Exporter("serverName", offlineMessagesStore, vCardManager, privateStorage, userManager, rosterItemProvider);
InputStream stream = this.getClass().getResourceAsStream("test-xepp227-import.xml");
assertTrue("Invalid input", testobject.validate(stream));
stream.close();
}
/**
* @throws IOException
* @throws DocumentException
*
*/
@Test
public void testImportUser() throws DocumentException, IOException {
logger.finest("testImportUser");
InExporter testobject = new Xep227Exporter("serverName", offlineMessagesStore, vCardManager, privateStorage, userManager, rosterItemProvider);
InputStream stream = this.getClass().getResourceAsStream("test-xepp227-import.xml");
String previousDomain = null;
boolean isUserProviderReadOnly = false;
List<String> res = testobject.importUsers(stream, previousDomain, isUserProviderReadOnly);
assertNotNull(res);
assertEquals(0, res.size());
stream.close();
Collection<User> users = userManager.getUsers();
assertEquals(4, users.size());
}
/**
* Test if XInclude is working
*
* @throws IOException
* @throws DocumentException
*
*/
@Test
public void testImportXInclude() throws DocumentException, IOException {
logger.finest("testImportIncludeUser");
InExporter testobject = new Xep227Exporter("serverName", offlineMessagesStore, vCardManager, privateStorage, userManager, rosterItemProvider);
String IMPORT_FILE_NAME = "/test-export-xinclude/xep227.xml";
URL streamurl = this.getClass().getResource(IMPORT_FILE_NAME);
assertNotNull(streamurl);
logger.fine("testImportIncludeUser:"+ streamurl.getFile());
InputStream stream = new FileInputStream(streamurl.getFile());
assertTrue("Invalid input", testobject.validate(stream));
stream.close();
String previousDomain = null;
boolean isUserProviderReadOnly = false;
stream = this.getClass().getResourceAsStream(IMPORT_FILE_NAME);
List<String> res = testobject.importUsers(stream, previousDomain, isUserProviderReadOnly);
assertNotNull(res);
assertEquals(0, res.size());
stream.close();
Collection<User> users = userManager.getUsers();
assertEquals(2, users.size());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<Openfire>
<User>
<Username>admin</Username>
<Password>admin</Password>
<Email>admin@example.com</Email>
<Name>Administrator</Name>
<CreationDate>1450103443961</CreationDate>
<ModifiedDate>0</ModifiedDate>
<Roster/>
</User>
<User>
<Username>anno</Username>
<Password>anno</Password>
<Email></Email>
<Name>A.A.M. Vliet</Name>
<CreationDate>1450104179268</CreationDate>
<ModifiedDate>1450104179268</ModifiedDate>
<Roster>
<Item jid="test1@openfire.xmpp.test" askstatus="-1" recvstatus="-1" substatus="3" name="Tester Een"/>
</Roster>
</User>
<User>
<Username>test1</Username>
<Password>test</Password>
<Email></Email>
<Name>Tester Een</Name>
<CreationDate>1450104197255</CreationDate>
<ModifiedDate>1450104197255</ModifiedDate>
<Roster>
<Item jid="anno@openfire.xmpp.test" askstatus="-1" recvstatus="-1" substatus="3"/>
</Roster>
</User>
<User>
<Username>test2</Username>
<Password>test</Password>
<Email></Email>
<Name>Tester Twee</Name>
<CreationDate>1450104215316</CreationDate>
<ModifiedDate>1450104215316</ModifiedDate>
<Roster/>
</User>
</Openfire>
<?xml version="1.0" encoding="UTF-8"?>
<!--
This file stores bootstrap properties needed by Openfire.
Property names must be in the format: "prop.name.is.blah=value"
That will be stored as:
<prop>
<name>
<is>
<blah>value</blah>
</is>
</name>
</prop>
Most properties are stored in the Openfire database. A
property viewer and editor is included in the admin console.
-->
<!-- root element, all properties must be under this element -->
<jive>
<adminConsole>
<!-- Disable either port by setting the value to -1 -->
<port>9090</port>
<securePort>9091</securePort>
</adminConsole>
<admin>
<!-- Use this section to define users that will have admin privileges. Below,
you will find two ways to specify which users are admins. Admins will
have access to the admin console (only local users) and may have also access
to other functionalities like ad-hoc commands. -->
<!-- By default, only the user with the username "admin" can login
to the admin console. Alternatively, you can specify a comma-delimitted
list usernames that should be authorized to login to the admin console
by setting the <authorizedUsernames> field below. -->
<!-- <authorizedUsernames></authorizedUsernames> -->
<!-- Comma-delimitted list of bare JIDs. The JIDs may belong to local
or remote users. -->
<!-- <authorizedJIDs></authorizedJIDs> -->
</admin>
<locale>en</locale>
<!-- Network settings. By default, Openfire will bind to all network interfaces.
Alternatively, you can specify a specific network interfaces that the server
will listen on. For example, 127.0.0.1. This setting is generally only useful
on multi-homed servers. -->
<!--
<network>
<interface></interface>
</network>
-->
<!-- Example LDAP settings -->
<!-- Note, for Active Directory, try usernameField=sAMAccountName, nameField=displayName,
emailField=mail -->
<!--
<ldap>
<host></host>
<port>389</port>
<usernameField>uid</usernameField>
<nameField>cn</nameField>
<emailField>mail</emailField>
<baseDN></baseDN>
<adminDN></adminDN>
<adminPassword></adminPassword>
</ldap>
<provider>
<user>
<className>org.jivesoftware.openfire.ldap.LdapUserProvider</className>
</user>
<auth>
<className>org.jivesoftware.openfire.ldap.LdapAuthenticationProvider</className>
</auth>
</provider>
-->
<!-- End example LDAP settings -->
<connectionProvider>
<className>org.jivesoftware.database.EmbeddedConnectionProvider</className>
</connectionProvider>
<setup>true</setup>
</jive>
<?xml version='1.0' encoding='UTF-8'?>
<user name='admin' password='admin' xmlns='urn:xmpp:pie:0'>
<query xmlns='jabber:iq:private'>
<mlink-wtmp-auth xmlns='mlink-wtmp-auth'>
<wtmp xmlns='http://isode.com/mlink/wtmp' type='auth'>
<trace-info jid='admin@mlink.xmpp.test' remote-address='127.0.0.1' conn-id='2' stamp='2016-09-23T11:06:11Z' />
<text>User admin@mlink.xmpp.test from [127.0.0.1] authenticated at 2016-09-23T11:06:11Z</text>
</wtmp>
</mlink-wtmp-auth>
</query>
<presence xmlns='jabber:client' type='subscribe' from='anno@mlink.xmpp.test'></presence>
<vCard xmlns='vcard-temp'>
<FN>admin</FN>
<N>
<FAMILY></FAMILY>
</N>
<EMAIL>
<INTERNET />
<WORK />
<USERID>admin@mlink.xmpp.test</USERID>
</EMAIL>
<DESC>This user has full control over the DSA</DESC>
</vCard>
</user>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<user name='anno' password='anno' xmlns='urn:xmpp:pie:0'>
<query xmlns='jabber:iq:roster' ver='37'>
<item jid='test1@mlink.xmpp.test' name='A Test' subscription='none' ask='subscribe'>
<group>testgroup</group>
</item>
<item jid='ju00122@mlink.xmpp.test' name='JU:00122' subscription='none' ask='subscribe' />
<item jid='ju001@mlink.xmpp.test' name='JU:001' subscription='none' ask='subscribe' />
<item jid='ju0011@mlink.xmpp.test' name='JU:0011' subscription='none' ask='subscribe'>
<group>testgroup</group>
</item>
</query>
<query xmlns='jabber:iq:private'>
<mlink-wtmp-auth xmlns='mlink-wtmp-auth'>
<wtmp xmlns='http://isode.com/mlink/wtmp' type='auth'>
<trace-info jid='anno@mlink.xmpp.test' remote-address='192.41.140.2' conn-id='8' stamp='2016-09-23T11:59:49Z' />
<text>User anno@mlink.xmpp.test from [192.41.140.2] authenticated at 2016-09-23T11:59:49Z</text>
</wtmp>
</mlink-wtmp-auth>
</query>
<query xmlns='jabber:iq:private'>
<storage xmlns='storage:bookmarks'>
<conference name="room1@conference.mlink.xmpp.test" jid="room1@conference.mlink.xmpp.test" autojoin="1">
<nick xmlns="storage:bookmarks">anno</nick>
</conference>
<conference name="xtg-room@conference.mlink.xmpp.test" jid="xtg-room@conference.mlink.xmpp.test" autojoin="1">
<nick xmlns="storage:bookmarks">anno</nick>
</conference>
</storage>
</query>
<vCard xmlns='vcard-temp'>
<FN>JChat XMPP to Tactical data-link Gateway (XTG)</FN>
<N>
<FAMILY>JChat XMPP to Tactical data-link Gateway (XTG)</FAMILY>
</N>
<NICKNAME>JChat XMPP to Tactical data-link Gateway (XTG)</NICKNAME>
<URL>www.home.nl-opop</URL>
<EMAIL>
<INTERNET />
<HOME />
<USERID>anno@mlink.xmpp.test</USERID>
</EMAIL>
<EMAIL>
<INTERNET />
<WORK />
<USERID>anno@mlink.xmpp.test</USERID>
</EMAIL>
<TEL>
<VOICE />
<HOME />
<NUMBER>123123123</NUMBER>
</TEL>
</vCard>
</user>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<user xmlns='urn:xmpp:pie:0' name='ju001'>
<query xmlns='jabber:iq:private'>
<mlink-wtmp-auth xmlns='mlink-wtmp-auth'>
<wtmp xmlns='http://isode.com/mlink/wtmp' type='auth'>
<trace-info jid='ju001@mlink.xmpp.test' remote-address='127.0.0.1' conn-id='83' stamp='2016-08-17T15:40:11Z' />
<text>User ju001@mlink.xmpp.test from [127.0.0.1] authenticated at 2016-08-17T15:40:11Z</text>
</wtmp>
</mlink-wtmp-auth>
</query>
<query xmlns='jabber:iq:private'>
<storage xmlns='storage:bookmarks'>
<conference name='whiteboard' autojoin='true' jid='whiteboard@conference.mlink.xmpp.test'></conference>
</storage>
</query>
<presence xmlns='jabber:client' type='subscribe' from='anno@mlink.xmpp.test'></presence>
<vCard xmlns='vcard-temp'>
<FN>JU:001</FN>
<N>
<FAMILY>JU:001</FAMILY>
</N>
<EMAIL>
<INTERNET />
<WORK />
<USERID>ju001@mlink.xmpp.test</USERID>
</EMAIL>
</vCard>
</user>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<user xmlns='urn:xmpp:pie:0' name='ju0011'>
<offline-messages>
<message xmlns="jabber:client" from='anno@mlink.xmpp.test/45da1d15b2641fba' to='ju0011@mlink.xmpp.test' id='39501f49-7505-481a-9a82-d3607091fef0'>
<active xmlns="http://jabber.org/protocol/chatstates" />
</message>
<message xmlns="jabber:client" from='anno@mlink.xmpp.test/45da1d15b2641fba' to='ju0011@mlink.xmpp.test' id='e3a86f1b-1a01-45b3-bd67-e3a0f6675149'>
<inactive xmlns="http://jabber.org/protocol/chatstates" />
</message>
</offline-messages>
<presence xmlns='jabber:client' type='subscribe' from='anno@mlink.xmpp.test'></presence>
<vCard xmlns='vcard-temp'>
<FN>JU:0011</FN>
<N>
<FAMILY>JU:0011</FAMILY>
</N>
<EMAIL>
<INTERNET />
<WORK />
<USERID>ju0011@mlink.xmpp.test</USERID>
</EMAIL>
</vCard>
</user>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<user xmlns='urn:xmpp:pie:0' name='ju0012'><vCard xmlns='vcard-temp'><FN>JU:0012</FN><N><FAMILY>JU:0012</FAMILY></N><EMAIL><INTERNET/><WORK/><USERID>ju0012@mlink.xmpp.test</USERID></EMAIL></vCard></user>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<user xmlns='urn:xmpp:pie:0' name='ju00122'>
<offline-messages>
<message xmlns="jabber:client" from='anno@mlink.xmpp.test/45da1d15b2641fba' to='ju00122@mlink.xmpp.test' id='44ec3d48-4719-4b52-89a6-dbd04a9399e7'>
<active xmlns="http://jabber.org/protocol/chatstates" />
</message>
<message xmlns="jabber:client" from='anno@mlink.xmpp.test/45da1d15b2641fba' to='ju00122@mlink.xmpp.test' id='db09c3a5-0bb6-403d-a284-8a805d32a6c7'>
<inactive xmlns="http://jabber.org/protocol/chatstates" />
</message>
</offline-messages>
<query xmlns='jabber:iq:private'>
<mlink-wtmp-auth xmlns='mlink-wtmp-auth'>
<wtmp xmlns='http://isode.com/mlink/wtmp' type='auth'>
<trace-info jid='ju00122@mlink.xmpp.test' remote-address='127.0.0.1' conn-id='67' stamp='2016-05-31T16:23:41Z' />
<text>User ju00122@mlink.xmpp.test from [127.0.0.1] authenticated at 2016-05-31T16:23:41Z</text>
</wtmp>
</mlink-wtmp-auth>
</query>
<presence xmlns='jabber:client' type='subscribe' from='test1@mlink.xmpp.test'></presence>
<presence xmlns='jabber:client' type='subscribe' from='anno@mlink.xmpp.test'></presence>
<vCard xmlns='vcard-temp'>
<FN>JU:00122</FN>
<N>
<FAMILY>JU:00122</FAMILY>
</N>
<NICKNAME>JU:00122</NICKNAME>
<EMAIL>
<INTERNET />
<WORK />
<USERID>ju00122@mlink.xmpp.test</USERID>
</EMAIL>
</vCard>
</user>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<user xmlns='urn:xmpp:pie:0' name='test1'>
<offline-messages>
<message xmlns="jabber:client" from='anno@mlink.xmpp.test/45da1d15b2641fba' to='test1@mlink.xmpp.test' id='e4ccbadc-29ed-44f6-af50-d887714b1a1b'>
<active xmlns="http://jabber.org/protocol/chatstates" />
</message>
<message xmlns="jabber:client" from='anno@mlink.xmpp.test/45da1d15b2641fba' to='test1@mlink.xmpp.test' id='18089241-4573-4532-9186-b354339a8683'>
<inactive xmlns="http://jabber.org/protocol/chatstates" />
</message>
<message xmlns="jabber:client" from='anno@mlink.xmpp.test/45da1d15b2641fba' to='test1@mlink.xmpp.test' id='10a862ba-2535-44ae-8319-3c4125e8c556'>
<active xmlns="http://jabber.org/protocol/chatstates" />
</message>
</offline-messages>
<query xmlns='jabber:iq:roster' ver='28'>
<item jid='test2@mlink.xmpp.test' subscription='both' />
<item jid='ju00144@mlink.xmpp.test' name='JU:00144' subscription='none' ask='subscribe'>
<group>test-group</group>
</item>
<item jid='ju00122@mlink.xmpp.test' name='JU:00122' subscription='none' ask='subscribe'>
<group>test-group</group>
</item>
<item jid='anno@openfire.xmpp.test' subscription='none' />
</query>
<query xmlns='jabber:iq:private'>
<mlink-wtmp-auth xmlns='mlink-wtmp-auth'>
<wtmp xmlns='http://isode.com/mlink/wtmp' type='auth'>
<trace-info jid='test1@mlink.xmpp.test' remote-address='127.0.0.1' conn-id='13' stamp='2016-08-24T11:50:45Z' />
<text>User test1@mlink.xmpp.test from [127.0.0.1] authenticated at 2016-08-24T11:50:45Z</text>
</wtmp>
</mlink-wtmp-auth>
</query>
<query xmlns='jabber:iq:private'>
<storage xmlns='storage:bookmarks'>
<url name='room1 -fav' url='room1@rooms.mlink.xmpp.test' />
<conference name='room1' autojoin='true' jid='room1@rooms.mlink.xmpp.test'></conference>
<conference name='room2' autojoin='true' jid='room2@rooms.mlink.xmpp.test'></conference>
<conference name='whiteboard' autojoin='true' jid='whiteboard@conference.mlink.xmpp.test'></conference>
</storage>
</query>
<presence xmlns='jabber:client' type='subscribe' from='anno@mlink.xmpp.test'></presence>
<vCard xmlns='vcard-temp'>
<FN>test1@mlink.xmpp.test</FN>
<N>
<PREFIX>Mr</PREFIX>
<GIVEN>A</GIVEN>
<MIDDLE>Sr</MIDDLE>
<FAMILY>Test</FAMILY>
<SUFFIX>van</SUFFIX>
</N>
<NICKNAME>swift-test</NICKNAME>
<EMAIL>
<INTERNET />
<WORK />
<USERID>test1@mlink.xmpp.test</USERID>
</EMAIL>
</vCard>
</user>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<user xmlns='urn:xmpp:pie:0' name='test2'><query xmlns='jabber:iq:roster' ver='8'><item jid='test1@mlink.xmpp.test' name='test1@mlink.xmpp.test' subscription='both'/><item jid='anno@mlink.xmpp.test' subscription='none'/></query><vCard xmlns='vcard-temp'><FN>test2@mlink.xmpp.test</FN><N><FAMILY>test2@mlink.xmpp.test</FAMILY></N><EMAIL><INTERNET/><WORK/><USERID>test2@mlink.xmpp.test</USERID></EMAIL></vCard></user>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<host xmlns='urn:xmpp:pie:0' xmlns:xi='http://www.w3.org/2001/XInclude' jid='sub'>
<xi:include href='sub/61/admin.xml' />
<xi:include href='sub/61/anno.xml' />
<xi:include href='sub/6a/ju001.xml' />
<xi:include href='sub/6a/ju0011.xml' />
<xi:include href='sub/6a/ju0012.xml' />
<xi:include href='sub/6a/ju00122.xml' />
<xi:include href='sub/74/test1.xml' />
<xi:include href='sub/74/test2.xml' />
</host>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8'?>
<server-data xmlns='urn:xmpp:pie:0' xmlns:xi='http://www.w3.org/2001/XInclude'>
<xi:include href='src/plugins/userImportExport/src/test/java/test-export-xinclude/test-import.xml' />
</server-data>
\ No newline at end of file
<%@ page import="java.io.OutputStream, <%@ page import="java.io.OutputStream,
org.jivesoftware.util.ParamUtils,
org.jivesoftware.openfire.XMPPServer, org.jivesoftware.openfire.XMPPServer,
org.jivesoftware.openfire.plugin.ImportExportPlugin" org.jivesoftware.openfire.plugin.ImportExportPlugin"
contentType="application/x-download"%><% contentType="application/x-download"%><%
String fileName = request.getParameter("fileName"); String fileName = request.getParameter("fileName");
boolean xep227Support = ParamUtils.getBooleanParameter(request, "xep227support", false);
response.setContentType("application/x-download"); response.setContentType("application/x-download");
response.setHeader("Content-Disposition","attachment;filename="+fileName+".xml"); response.setHeader("Content-Disposition","attachment;filename="+fileName+".xml");
ImportExportPlugin plugin = (ImportExportPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("userimportexport"); ImportExportPlugin plugin = (ImportExportPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("userimportexport");
byte[] content = plugin.exportUsersToByteArray(); byte[] content = plugin.exportUsersToByteArray(xep227Support);
OutputStream os = response.getOutputStream(); OutputStream os = response.getOutputStream();
os.write(content); os.write(content);
os.flush(); os.flush();
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
boolean exportUsers = request.getParameter("exportUsers") != null; boolean exportUsers = request.getParameter("exportUsers") != null;
boolean success = request.getParameter("success") != null; boolean success = request.getParameter("success") != null;
boolean exportToFile = ParamUtils.getBooleanParameter(request, "exporttofile", true); boolean exportToFile = ParamUtils.getBooleanParameter(request, "exporttofile", true);
boolean xep227Support = ParamUtils.getBooleanParameter(request, "xep227support", false);
ImportExportPlugin plugin = (ImportExportPlugin)XMPPServer.getInstance().getPluginManager( ImportExportPlugin plugin = (ImportExportPlugin)XMPPServer.getInstance().getPluginManager(
).getPlugin("userimportexport"); ).getPlugin("userimportexport");
...@@ -26,12 +27,12 @@ ...@@ -26,12 +27,12 @@
errors.put("missingFile","missingFile"); errors.put("missingFile","missingFile");
} }
else { else {
response.sendRedirect("export-file.jsp?fileName="+file); response.sendRedirect("export-file.jsp?fileName="+file + ( xep227Support ? "&xep227support=true" : ""));
} }
} }
else { else {
try { try {
exportText = plugin.exportUsersToString(); exportText = plugin.exportUsersToString(xep227Support);
} }
catch (IOException e) { catch (IOException e) {
errors.put("IOException","IOException"); errors.put("IOException","IOException");
...@@ -94,7 +95,7 @@ ...@@ -94,7 +95,7 @@
<table cellpadding="3" cellspacing="0" border="0" width="100%"> <table cellpadding="3" cellspacing="0" border="0" width="100%">
<tbody> <tbody>
<tr> <tr>
<td width="1%"><input type="radio" name="exporttofile" value="true" <%= exportToFile ? "checked" : "" %> id="rb01"></td> <td width="1%"><input type="radio" name="exporttofile" value="true" <%= exportToFile ? "checked" : "" %> id="rb01"/></td>
<td width="99%"><label for="rb01"><b>To File</b></label> - Save user data to the specified file location.</td> <td width="99%"><label for="rb01"><b>To File</b></label> - Save user data to the specified file location.</td>
</tr> </tr>
<tr> <tr>
...@@ -102,13 +103,17 @@ ...@@ -102,13 +103,17 @@
<td width="99%">Export File Name:&nbsp;<input type="text" size="30" maxlength="150" name="exportFile"></td> <td width="99%">Export File Name:&nbsp;<input type="text" size="30" maxlength="150" name="exportFile"></td>
</tr> </tr>
<tr> <tr>
<td width="1%"><input type="radio" name="exporttofile" value="false" <%= !exportToFile ? "checked" : "" %> id="rb02"></td> <td width="1%"><input type="radio" name="exporttofile" value="false" <%= !exportToFile ? "checked" : "" %> id="rb02"/></td>
<td width="99%"><label for="rb02"><b>To Screen</b></label> - Display user data in the text area below.</td> <td width="99%"><label for="rb02"><b>To Screen</b></label> - Display user data in the text area below.</td>
</tr> </tr>
<tr> <tr>
<td width="1%">&nbsp;</td> <td width="1%">&nbsp;</td>
<td width="99%"><textarea cols="80" rows="20" wrap=off><%=exportText %></textarea></td> <td width="99%"><textarea cols="80" rows="20" wrap=off><%=exportText %></textarea></td>
</tr> </tr>
<tr>
<td width="1%"><input type="checkbox" name="xep227support" <%= xep227Support ? "checked" : "" %> id="rb03"/></td>
<td width="99%"><label for="rb03"><b>XEP-0227</b></label> - Export using format defined in <a href="http://www.xmpp.org/extensions/xep-0227.html" target="_blank">XEP-0227</a>.</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
......
...@@ -53,6 +53,11 @@ ...@@ -53,6 +53,11 @@
url="system-email.jsp" url="system-email.jsp"
description="${sidebar.server-email.descr}"/> description="${sidebar.server-email.descr}"/>
<!-- SMS -->
<item id="system-sms" name="${sidebar.server-sms}"
url="system-sms.jsp"
description="${sidebar.server-sms.descr}"/>
<!-- Security Audit Viewer --> <!-- Security Audit Viewer -->
<item id="security-audit-viewer" name="${sidebar.security-audit-viewer}" <item id="security-audit-viewer" name="${sidebar.security-audit-viewer}"
url="security-audit-viewer.jsp" url="security-audit-viewer.jsp"
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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