This commit is contained in:
parent
b9357194a3
commit
ce8f97b55f
|
@ -1,192 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>User Import/Export Plugin Changelog</title>
|
||||
<style type="text/css">
|
||||
/* global font and body settings */
|
||||
body {
|
||||
font-size: 100%;
|
||||
background-color: #d3d6d9;
|
||||
padding: 0px;
|
||||
margin: 0px 0px 30px 0px;
|
||||
}
|
||||
|
||||
body,td,th {
|
||||
font-family: arial, helvetica, sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
pre,tt,code {
|
||||
font-family: courier new, monospaced;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
#pageContainer {
|
||||
display: block;
|
||||
position: relative;
|
||||
clear: both;
|
||||
background-color: #fff;
|
||||
border: 1px solid #999;
|
||||
padding: 40px;
|
||||
margin: 30px;
|
||||
-moz-border-radius: 6px;
|
||||
}
|
||||
|
||||
#pageHeader {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 80px;
|
||||
background-color: #e7eaee;
|
||||
border: 1px solid #cccccc;
|
||||
border-bottom: none;
|
||||
-moz-border-radius: 5px 5px 0px 0px;
|
||||
margin: 10px 0px 0px 0px;
|
||||
}
|
||||
|
||||
#pageBody {
|
||||
margin: 0px 18px 0px 20px;
|
||||
}
|
||||
|
||||
/* anchors */
|
||||
a:link {
|
||||
color: #11568c;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #571c8d;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #7a1d42;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #7a1d42;
|
||||
}
|
||||
|
||||
/* page header elements (logo and navigation) */
|
||||
.navigation {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 20px;
|
||||
background-color: #335588;
|
||||
border: 1px solid #cccccc;
|
||||
border-top: none;
|
||||
color: #ffffff;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
margin: 0px 0px 25px 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.navigation a {
|
||||
margin: 0px 20px 0px 20px;
|
||||
}
|
||||
|
||||
.navigation a:link {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.navigation a:visited {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.navigation a:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.navigation a:active {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* headings */
|
||||
h1 {
|
||||
display: block;
|
||||
position: relative;
|
||||
font-size: 1.7em;
|
||||
font-weight: bold;
|
||||
color: #670e15;
|
||||
padding: 0px;
|
||||
margin: 30px 0px 0px 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
margin: 40px 0px 6px 0px;
|
||||
padding: 0px;
|
||||
color: #335588;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.0em;
|
||||
font-weight: bold;
|
||||
margin: 25px 0px 3px 0px;
|
||||
padding: 0px;
|
||||
color: #334466;
|
||||
}
|
||||
|
||||
/* general elements */
|
||||
p {
|
||||
margin: 0px 0px 15px 0px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 5px 0px 15px 35px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
tt {
|
||||
font-family: courier new;
|
||||
font-weight: bold;
|
||||
color: #060;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
background-color: #999999;
|
||||
border: none;
|
||||
margin: 40px 0px 20px 0px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-size: 8pt;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
margin-top: 2em;
|
||||
padding-top: 0.5em;
|
||||
border-top: 1px #CCC solid;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="pageContainer">
|
||||
<div id="pageHeader">
|
||||
<h1>User Import/Export XEP-0227 compliant Plugin Changelog</h1>
|
||||
</div>
|
||||
<div id="pageBody">
|
||||
<br/>
|
||||
<p><b>Note:</b> Openfire plugin improved to export to XEP-227
|
||||
format. There is also old code to import users, but using a custom Openfire
|
||||
format. If you are interested you can improve it to also support XEP-227
|
||||
import.</p>
|
||||
|
||||
<h2>0.0.1 -- <span style="font-weight: normal;">August 3,
|
||||
2009</span></h2>
|
||||
<ul>
|
||||
<li>Initial release.</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,79 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
||||
<xs:element name="Openfire">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="User" maxOccurs="unbounded" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="User">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element ref="Username"/>
|
||||
<xs:element ref="Password"/>
|
||||
<xs:element ref="Email" />
|
||||
<xs:element ref="Name" />
|
||||
<xs:element ref="CreationDate" />
|
||||
<xs:element ref="ModifiedDate" />
|
||||
<xs:element ref="Roster" />
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Username" type="xs:string" />
|
||||
<xs:element name="Password" type="xs:string" />
|
||||
<xs:element name="Email" type="xs:string" />
|
||||
<xs:element name="Name" type="xs:string" />
|
||||
<xs:element name="CreationDate" type="jive-date" />
|
||||
<xs:element name="ModifiedDate" type="jive-date" />
|
||||
<xs:element name="Roster">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="Item" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Item">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="Group" maxOccurs="unbounded" />
|
||||
</xs:sequence>
|
||||
<xs:attribute ref="jid" use="required" />
|
||||
<xs:attribute name="askstatus" use="required" />
|
||||
<xs:attribute name="recvstatus" use="required" />
|
||||
<xs:attribute name="substatus" use="required" />
|
||||
<xs:attribute name="name" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Group" type="xs:string" />
|
||||
<xs:attribute name="jid" type="xs:string" />
|
||||
<xs:attribute name="name" type="xs:string" />
|
||||
<xs:attribute name="askstatus">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="-1" />
|
||||
<xs:maxInclusive value="3" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="recvstatus">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="-1" />
|
||||
<xs:maxInclusive value="3" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="substatus">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="-1" />
|
||||
<xs:maxInclusive value="3" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:simpleType name="jive-date">
|
||||
<xs:restriction base="xs:string"></xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
|
@ -1,47 +0,0 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
|
||||
<xs:schema
|
||||
xmlns:xs='http://www.w3.org/2001/XMLSchema'
|
||||
targetNamespace='http://www.xmpp.org/extensions/xep-0227.html#ns'
|
||||
xmlns='http://www.xmpp.org/extensions/xep-0227.html#ns'
|
||||
elementFormDefault='qualified'>
|
||||
|
||||
<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>
|
Binary file not shown.
|
@ -1,18 +0,0 @@
|
|||
|
||||
Copy in this directory the following files:
|
||||
|
||||
commons-fileupload-1.0.jar
|
||||
dom4j-1.6.1.jar
|
||||
isorelax.jar
|
||||
msv.jar
|
||||
relaxngDatatype.jar
|
||||
xsdlib.jar
|
||||
|
||||
You can get those files from the original Openfire plugin: userImportExport.jar
|
||||
|
||||
Note: openfire.jar file in this folder must be used if you want to export the
|
||||
private stored info also you will need to comment out lines 332 up to 335 in
|
||||
ExporterXEP227.java class and finally recompile it.
|
||||
Have fun !
|
||||
|
||||
---
|
Binary file not shown.
Before Width: | Height: | Size: 977 B |
Binary file not shown.
Before Width: | Height: | Size: 600 B |
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<plugin>
|
||||
<class>net.processone.plugin.openfire.ExporterXEP227</class>
|
||||
|
||||
<name>User Import Export XEP-0227</name>
|
||||
<description>Enables import and export of user data</description>
|
||||
<author>Vidal Santiago Martinez (original plugin: Ryan Graham)</author>
|
||||
<version>0.0.1</version>
|
||||
<date>03/08/2009</date>
|
||||
<minServerVersion>3.5.0</minServerVersion>
|
||||
|
||||
<adminconsole>
|
||||
<tab id="tab-users">
|
||||
<sidebar id="user-import-export" name="Import & Export XEP-0227 compliant ">
|
||||
<item id="import-export-selection" name="User Import & Export XEP-0227 compliant"
|
||||
url="import-export-selection.jsp"
|
||||
description="Allows the importing and exporting of Openfire user data in a XEP-0227 format." />
|
||||
</sidebar>
|
||||
</tab>
|
||||
</adminconsole>
|
||||
</plugin>
|
|
@ -1,262 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>User Import/Export XEP-0227 compliant Plugin Readme</title>
|
||||
<style type="text/css">
|
||||
/* global font and body settings */
|
||||
body {
|
||||
font-size : 100%;
|
||||
background-color : #d3d6d9;
|
||||
padding: 0px;
|
||||
margin: 0px 0px 30px 0px;
|
||||
}
|
||||
body, td, th {
|
||||
font-family : arial, helvetica, sans-serif;
|
||||
font-size : 10pt;
|
||||
}
|
||||
pre, tt, code {
|
||||
font-family : courier new, monospaced;
|
||||
font-size : 9pt;
|
||||
}
|
||||
#pageContainer {
|
||||
display: block;
|
||||
position: relative;
|
||||
clear: both;
|
||||
background-color: #fff;
|
||||
border: 1px solid #999;
|
||||
padding: 40px;
|
||||
margin: 30px;
|
||||
-moz-border-radius: 6px;
|
||||
}
|
||||
#pageHeader {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 80px;
|
||||
background-color: #e7eaee;
|
||||
border: 1px solid #cccccc;
|
||||
border-bottom: none;
|
||||
-moz-border-radius: 5px 5px 0px 0px;
|
||||
margin: 10px 0px 0px 0px;
|
||||
}
|
||||
#pageBody {
|
||||
margin: 0px 18px 0px 20px;
|
||||
}
|
||||
|
||||
/* anchors */
|
||||
a:link {
|
||||
color: #11568c;
|
||||
}
|
||||
a:visited {
|
||||
color: #571c8d;
|
||||
}
|
||||
a:hover {
|
||||
color: #7a1d42;
|
||||
text-decoration : underline;
|
||||
}
|
||||
a:active {
|
||||
color: #7a1d42;
|
||||
}
|
||||
|
||||
/* page header elements (logo and navigation) */
|
||||
.navigation {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 20px;
|
||||
background-color: #335588;
|
||||
border: 1px solid #cccccc;
|
||||
border-top: none;
|
||||
color: #ffffff;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
margin: 0px 0px 25px 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.navigation a {
|
||||
margin: 0px 20px 0px 20px;
|
||||
}
|
||||
.navigation a:link { color: #ffffff; }
|
||||
.navigation a:visited { color: #ffffff; }
|
||||
.navigation a:hover { color: #ffffff; }
|
||||
.navigation a:active { color: #ffffff; }
|
||||
|
||||
/* headings */
|
||||
h1 {
|
||||
display: block;
|
||||
position: relative;
|
||||
font-size : 1.7em;
|
||||
font-weight : bold;
|
||||
color: #670e15;
|
||||
padding: 0px;
|
||||
margin: 30px 0px 0px 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size : 1.3em;
|
||||
font-weight : bold;
|
||||
margin: 40px 0px 6px 0px;
|
||||
padding: 0px;
|
||||
color: #335588;
|
||||
}
|
||||
h3 {
|
||||
font-size : 1.0em;
|
||||
font-weight : bold;
|
||||
margin: 25px 0px 3px 0px;
|
||||
padding: 0px;
|
||||
color: #334466;
|
||||
}
|
||||
|
||||
/* general elements */
|
||||
p {
|
||||
margin: 0px 0px 15px 0px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 5px 0px 15px 35px;
|
||||
}
|
||||
li {
|
||||
padding-bottom : 4px;
|
||||
}
|
||||
tt {
|
||||
font-family : courier new;
|
||||
font-weight : bold;
|
||||
color : #060;
|
||||
}
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
background-color: #999999;
|
||||
border: none;
|
||||
margin: 40px 0px 20px 0px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-size : 8pt;
|
||||
color : #666;
|
||||
text-align : center;
|
||||
margin-top : 2em;
|
||||
padding-top : 0.5em;
|
||||
border-top : 1px #CCC solid;
|
||||
}
|
||||
|
||||
.datatable TH {
|
||||
color : #fff;
|
||||
background-color : #2A448C;
|
||||
text-align : left;
|
||||
}
|
||||
|
||||
.datatable TD {
|
||||
background-color : #FAF6EF;
|
||||
}
|
||||
|
||||
.datatable .name {
|
||||
background-color : #DCE2F5;
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
.xmltable TD {
|
||||
background-color : #EFEFEF;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="pageContainer">
|
||||
|
||||
<div id="pageHeader">
|
||||
<h1>User Import/Export XEP-0227 compliant Plugin Readme</h1>
|
||||
</div>
|
||||
<div id="pageBody">
|
||||
|
||||
<h2>Overview</h2>
|
||||
<p>The user import/export plugin provides a way to import and export Openfire user data via
|
||||
the Admin Console. This plugin use for its migration the XEP-0227 standard to be able to migrate its
|
||||
list of users from other Jabber/XMPP compliant based systems.</p>
|
||||
|
||||
<p>This plugin is based in the original plugin developed by Ryan Graham.
|
||||
It was modified by Vidal Santiago Martinez (from <a href="http://www.process-one.net/">ProcessOne</a>)
|
||||
to export to a XML file in the XEP-0227 format.
|
||||
If you are migrating to ejabberd, check additional details in:
|
||||
<a href="https://support.process-one.net/doc/display/MESSENGER/ejabberd+migration+kit">ejabberd migration kit</a>.
|
||||
The existing feature to import XML file was not modified, so it is not capable of importing XEP-0227 files.</p>
|
||||
|
||||
<h2>Installation</h2>
|
||||
<p>Copy the OpenfireExporter.jar into the plugins directory of your Openfire installation.
|
||||
The plugin will then be automatically deployed. To upgrade to a new version, copy the new
|
||||
OpenfireExporter.jar file over the existing file.</p>
|
||||
|
||||
<h2>Configuration</h2>
|
||||
<p>Nothing to do</p>
|
||||
|
||||
<h2>Using the Plugin</h2>
|
||||
<p>The plugin is accessed via the "User Import & Export XEP-0227 compliant" 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
|
||||
or POP3 this plugin will still work with two caveats:
|
||||
<ol>
|
||||
<li>When exporting, the username will be placed in the password element.</li>
|
||||
<li>When importing, no new users will be created but if the user exists in the user store their roster will be loaded.</li>
|
||||
<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
|
||||
information you want to locate and then click on the "Import" button. If the plugin is successful
|
||||
in importing all user data, you will be presented with the message: "All users added successfully".
|
||||
If the plugin was not successful in importing all user data you, will receive a message indicating
|
||||
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
|
||||
information, except in the case of using a read-only user store.</li>
|
||||
<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,
|
||||
select the "To File" radio button, enter the name you want your export file to be called in the
|
||||
"Export File Name" and then click on the "Export" button. Note: the plugin will automatically append
|
||||
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 provided text area.</li>
|
||||
<li><strong>Migration</strong> - To import user data from another instant messaging system using the plugin,
|
||||
the import file must conform to the wildfire-user-schema.xsd schema file (located in the classes
|
||||
directory of the OpenfireExporter.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
|
||||
not be imported. During the import process the plugin gives you the ability to update user roster
|
||||
entries domain names to server name of your Openfire installation. For example, say you have a user
|
||||
whose roster looks like:
|
||||
</li>
|
||||
</ol>
|
||||
<h2>Example</h2>
|
||||
<p>
|
||||
<div class="xmltable">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<?xml version="1.0" encoding="UTF-8"?><br/>
|
||||
<server-data xmlns="http://www.xmpp.org/extensions/xep-0227.html#ns"><br/>
|
||||
<host jid="example.org"><br/>
|
||||
<user name="testuser1" password="testuser1"><br/>
|
||||
<query xmlns="jabber:iq:roster"><br/>
|
||||
<item jid="testuser1@example.org" name="testuser1" subscription="both"><br/>
|
||||
<group/><br/>
|
||||
</item><br/>
|
||||
</query><br/>
|
||||
</user><br/>
|
||||
<user name="santiago" password="santiago"><br/>
|
||||
<query xmlns="jabber:iq:roster"><br/>
|
||||
<item jid="smartinez@example.org" name="santiago" subscription="both"><br/>
|
||||
<group/><br/>
|
||||
</item><br/>
|
||||
</query><br/>
|
||||
<vCard xmlns="vcard-temp"><br/>
|
||||
<FN>Vidal Santiago Martinez</FN><br/>
|
||||
<NICKNAME>Santiago</NICKNAME><br/>
|
||||
<EMAIL>smartinez@example.org</EMAIL><br/>
|
||||
<URL>www.process-one.net</URL><br/>
|
||||
</vCard><br/>
|
||||
</user><br/>
|
||||
</host><br/>
|
||||
</server-data><br/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,477 +0,0 @@
|
|||
package net.processone.plugin.openfire;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.DocumentHelper;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.OutputFormat;
|
||||
import org.dom4j.io.SAXReader;
|
||||
import org.dom4j.io.XMLWriter;
|
||||
import org.jivesoftware.openfire.OfflineMessage;
|
||||
import org.jivesoftware.openfire.OfflineMessageStore;
|
||||
import org.jivesoftware.openfire.PrivateStorage;
|
||||
import org.jivesoftware.openfire.XMPPServer;
|
||||
import org.jivesoftware.openfire.auth.AuthFactory;
|
||||
import org.jivesoftware.openfire.container.Plugin;
|
||||
import org.jivesoftware.openfire.container.PluginManager;
|
||||
import org.jivesoftware.openfire.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.user.UserNotFoundException;
|
||||
import org.jivesoftware.openfire.user.UserProvider;
|
||||
import org.jivesoftware.openfire.vcard.VCardManager;
|
||||
import org.jivesoftware.stringprep.Stringprep;
|
||||
import org.jivesoftware.stringprep.StringprepException;
|
||||
import org.jivesoftware.util.Log;
|
||||
import org.xmpp.packet.JID;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The user import/export plugin provides a way to import and export Openfire
|
||||
* user data via the Admin Console. This plugin is XEP-0227 compliant
|
||||
* </p>
|
||||
* This plugin can export: <li>User data</li> <li>vCard</li> <li>Offline
|
||||
* messages</li> <br/>
|
||||
* <p>
|
||||
* <b>See also:</b> {@link List http://xmpp.org/extensions/xep-0227.html}
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:smartinez@process-one.net">Vidal Santiago
|
||||
* Martinez</a>
|
||||
*/
|
||||
public class ExporterXEP227 implements Plugin {
|
||||
|
||||
private static final String LOAD_ALL_PRIVATE = "SELECT privateData FROM ofPrivate WHERE username=?";
|
||||
|
||||
private UserManager userManager;
|
||||
private UserProvider provider;
|
||||
private String serverName;
|
||||
private OfflineMessageStore offlineMessagesStore;
|
||||
private VCardManager vCardManager;
|
||||
private PrivateStorage privateStorage;
|
||||
|
||||
|
||||
public ExporterXEP227() {
|
||||
userManager = XMPPServer.getInstance().getUserManager();
|
||||
offlineMessagesStore = XMPPServer.getInstance()
|
||||
.getOfflineMessageStore();
|
||||
provider = UserManager.getUserProvider();
|
||||
serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
|
||||
privateStorage = XMPPServer.getInstance().getPrivateStorage();
|
||||
vCardManager = VCardManager.getInstance();
|
||||
}
|
||||
|
||||
public void initializePlugin(PluginManager manager, File pluginDirectory) {
|
||||
}
|
||||
|
||||
public void destroyPlugin() {
|
||||
userManager = null;
|
||||
provider = null;
|
||||
serverName = null;
|
||||
offlineMessagesStore = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that returns true if this UserProvider is read-only.
|
||||
*
|
||||
* @return true if the user provider is read-only.
|
||||
*/
|
||||
public boolean isUserProviderReadOnly() {
|
||||
return provider.isReadOnly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the user data that is to be exported to a byte[]. If a read-only
|
||||
* user store is being used a user's password will be the same as their
|
||||
* username.
|
||||
*
|
||||
* @return a byte[] of the user data.
|
||||
* @throws IOException
|
||||
* if there's a problem writing to the XMLWriter.
|
||||
*/
|
||||
public byte[] exportUsersToByteArray() throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
|
||||
writer.write(exportUsers());
|
||||
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the exported user data to a String. If a read-only user store is
|
||||
* being used a user's password will be the same as their username.
|
||||
*
|
||||
* @return a formatted String representation of the user data.
|
||||
* @throws IOException
|
||||
* if there's a problem writing to the XMLWriter.
|
||||
*/
|
||||
public String exportUsersToString() throws IOException {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
XMLWriter writer = null;
|
||||
try {
|
||||
writer = new XMLWriter(stringWriter, OutputFormat
|
||||
.createPrettyPrint());
|
||||
writer.write(exportUsers());
|
||||
} catch (IOException ioe) {
|
||||
Log.error(ioe);
|
||||
throw ioe;
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of usernames that were unable to be imported or whose
|
||||
* rosters could not imported. Users are not able to be imported for the
|
||||
* following reasons: <li>Their username is not properly formatted. <li>If a
|
||||
* read-only user data store is being used and the user could not be found.
|
||||
* <li>If a writeable user data store is being used and the user already
|
||||
* exists.
|
||||
*
|
||||
* @param file
|
||||
* a FileItem containing the user data to be imported.
|
||||
* @param previousDomain
|
||||
* a String an optional parameter that if supplied will replace
|
||||
* the user roster entries domain names to server name of current
|
||||
* Openfire installation.
|
||||
* @return True if FileItem matches the openfire user schema.
|
||||
* @throws IOException
|
||||
* if there is a problem reading the FileItem.
|
||||
* @throws DocumentException
|
||||
* if an error occurs during parsing.
|
||||
*/
|
||||
public List<String> importUserData(FileItem file, String previousDomain)
|
||||
throws DocumentException, IOException {
|
||||
SAXReader reader = new SAXReader();
|
||||
Document document = reader.read(file.getInputStream());
|
||||
return importUsers(document, previousDomain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the supplied FileItem matches the openfire user
|
||||
* schema
|
||||
*
|
||||
* @param file
|
||||
* a FileItem to be validated.
|
||||
* @return True if FileItem matches the openfire user schema.
|
||||
*/
|
||||
public boolean validateImportFile(FileItem file) {
|
||||
try {
|
||||
return new UserSchemaValidator(file, "wildfire-user-schema.xsd")
|
||||
.validate();
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding heading of an user and its parameters
|
||||
*
|
||||
* @param userElement
|
||||
* DOM element
|
||||
* @param user
|
||||
* User object
|
||||
*/
|
||||
private void addUser(Element userElement, User user) {
|
||||
|
||||
String userName = user.getUsername();
|
||||
userElement.addAttribute("name", userName);
|
||||
|
||||
try {
|
||||
userElement.addAttribute("password", AuthFactory
|
||||
.getPassword(userName));
|
||||
|
||||
} catch (UserNotFoundException e) {
|
||||
Log.info("User " + userName
|
||||
+ " not found, setting their password to their username");
|
||||
userElement.addAttribute("password", userName);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
Log.info("Unable to retrieve " + userName
|
||||
+ " password, setting their password to their username");
|
||||
userElement.addAttribute("password", userName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add roster and its groups to a DOM element
|
||||
*
|
||||
* @param userElement
|
||||
* DOM element
|
||||
* @param user
|
||||
* User
|
||||
*/
|
||||
private void addRoster(Element userElement, User user) {
|
||||
Element rosterElement = userElement.addElement("query",
|
||||
"jabber:iq:roster");
|
||||
|
||||
Collection<RosterItem> roster = user.getRoster().getRosterItems();
|
||||
for (RosterItem ri : roster) {
|
||||
Element itemElement = rosterElement.addElement("item");
|
||||
|
||||
itemElement.addAttribute("jid", ri.getJid().toBareJID());
|
||||
itemElement.addAttribute("name", ri.getNickname());
|
||||
itemElement.addAttribute("subscription", ri.getSubStatus()
|
||||
.getName());
|
||||
|
||||
/**
|
||||
* Adding groups
|
||||
*/
|
||||
Element groupElement = itemElement.addElement("group");
|
||||
List<String> groups = ri.getGroups();
|
||||
for (String group : groups) {
|
||||
groupElement.addText(group);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding offline messages, if there are some.
|
||||
*
|
||||
* @param hostname
|
||||
* host name
|
||||
* @param userElement
|
||||
* DOM element
|
||||
* @param userName
|
||||
* user name
|
||||
*/
|
||||
private void addOfflineMessages(String hostname, Element userElement,
|
||||
String userName) {
|
||||
Collection<OfflineMessage> offlineMessages = offlineMessagesStore
|
||||
.getMessages(userName, false);
|
||||
|
||||
if (!offlineMessages.isEmpty()) {
|
||||
Element offlineElement = userElement.addElement("offline-messages");
|
||||
|
||||
for (OfflineMessage offMessage : offlineMessages) {
|
||||
Element messageElement = offlineElement.addElement("message",
|
||||
"jabber:client");
|
||||
messageElement.addAttribute("from", offMessage.getFrom()
|
||||
.toString());
|
||||
messageElement
|
||||
.addAttribute("to", offMessage.getTo().toString());
|
||||
messageElement
|
||||
.addAttribute("type", offMessage.getType().name());
|
||||
|
||||
/**
|
||||
* Adding text message
|
||||
*/
|
||||
Element bodyElement = messageElement.addElement("body");
|
||||
String body = offMessage.getBody();
|
||||
bodyElement.addText(body != null ? body : "");
|
||||
|
||||
/**
|
||||
* Adding delay element
|
||||
*/
|
||||
Element delayElement = messageElement.addElement("delay",
|
||||
"urn:xmpp:delay");
|
||||
delayElement.addAttribute("from", hostname);
|
||||
delayElement.addAttribute("stamp", offMessage.getCreationDate()
|
||||
.toString());
|
||||
delayElement.addText("Offline Storage");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding vcard element
|
||||
*
|
||||
* @param userElement
|
||||
* DOM element
|
||||
* @param userName
|
||||
* user name
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addVCard(Element userElement, String userName) {
|
||||
Element vCard = vCardManager.getVCard(userName);
|
||||
if (vCard != null) {
|
||||
Element vCardElement = userElement
|
||||
.addElement("vCard", "vcard-temp");
|
||||
for (Iterator<Element> iterator = vCard.elementIterator(); iterator
|
||||
.hasNext();) {
|
||||
Element element = iterator.next();
|
||||
vCardElement.addElement(element.getName()).addText(
|
||||
element.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the private stored information (XEP-0049)
|
||||
* <b>Note: this method is not suported in the available openfire releases,
|
||||
* If you want to use it, you will need to change your openfire.jar file to
|
||||
* openfire.jar file in this projects lib folder, remove comment to this
|
||||
* method and recompile the plugin.
|
||||
*
|
||||
* </b>
|
||||
* @param userName User name
|
||||
* @param userElement User element
|
||||
*/
|
||||
private void addPrivateStorage(String userName, Element userElement) {
|
||||
// Element result = privateStorage.getAll(userName);
|
||||
// if (result.elements().size() > 0) {
|
||||
// userElement.add(result.createCopy());
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the user list and its, Vcard, Offline mesages and roster list to
|
||||
* an XML representation XEP-0227 compliant
|
||||
*
|
||||
* @return DOM document
|
||||
*/
|
||||
private Document exportUsers() {
|
||||
Document document = DocumentHelper.createDocument();
|
||||
|
||||
Element root = document.addElement("server-data",
|
||||
"http://www.xmpp.org/extensions/xep-0227.html#ns");
|
||||
|
||||
Element host = root.addElement("host");
|
||||
|
||||
host.addAttribute("jid", serverName);
|
||||
|
||||
Collection<User> users = userManager.getUsers();
|
||||
for (User user : users) {
|
||||
|
||||
String userName = user.getUsername();
|
||||
|
||||
Element userElement = host.addElement("user");
|
||||
|
||||
addUser(userElement, user);
|
||||
|
||||
addRoster(userElement, user);
|
||||
|
||||
addVCard(userElement, userName);
|
||||
|
||||
addOfflineMessages(serverName, userElement, userName);
|
||||
|
||||
addPrivateStorage(userName, userElement);
|
||||
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
private List<String> importUsers(Document document, String previousDomain) {
|
||||
List<String> invalidUsers = new ArrayList<String>();
|
||||
|
||||
UserManager userManager = UserManager.getInstance();
|
||||
RosterItemProvider rosterItemProvider = RosterItemProvider
|
||||
.getInstance();
|
||||
|
||||
Element users = document.getRootElement();
|
||||
|
||||
Iterator usersIter = users.elementIterator("User");
|
||||
while (usersIter.hasNext()) {
|
||||
Element user = (Element) usersIter.next();
|
||||
|
||||
String userName = null;
|
||||
String password = null;
|
||||
String email = null;
|
||||
String name = null;
|
||||
List<RosterItem> rosterItems = new ArrayList<RosterItem>();
|
||||
|
||||
Iterator userElements = user.elementIterator();
|
||||
while (userElements.hasNext()) {
|
||||
Element userElement = (Element) 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 rosterIter = userElement.elementIterator("Item");
|
||||
|
||||
while (rosterIter.hasNext()) {
|
||||
Element rosterElement = (Element) 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 groupIter = rosterElement
|
||||
.elementIterator("Group");
|
||||
while (groupIter.hasNext()) {
|
||||
Element group = (Element) groupIter.next();
|
||||
groups.add(group.getText());
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package net.processone.plugin.openfire;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
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.jivesoftware.util.Log;
|
||||
import org.xml.sax.ContentHandler;
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.Locator;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
import com.sun.msv.reader.util.GrammarLoader;
|
||||
import com.sun.msv.reader.util.IgnoreController;
|
||||
import com.sun.msv.verifier.DocumentDeclaration;
|
||||
import com.sun.msv.verifier.Verifier;
|
||||
|
||||
public class UserSchemaValidator {
|
||||
private Document doc;
|
||||
private String schema;
|
||||
|
||||
UserSchemaValidator(FileItem usersFile, String schemaFile) throws DocumentException, IOException {
|
||||
SAXReader reader = new SAXReader();
|
||||
doc = reader.read(usersFile.getInputStream());
|
||||
|
||||
URL schemaURL = this.getClass().getClassLoader().getResource(schemaFile);
|
||||
schema = schemaURL.toExternalForm();
|
||||
}
|
||||
|
||||
boolean validate() {
|
||||
try {
|
||||
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
|
||||
saxFactory.setNamespaceAware(true);
|
||||
DocumentDeclaration docDeclaration = GrammarLoader.loadVGM(schema, new IgnoreController() {
|
||||
public void error(Locator[] locations,
|
||||
String message,
|
||||
Exception exception) {
|
||||
Log.error("ERROR: " + message);
|
||||
}
|
||||
|
||||
public void error(Locator[] locations, String message) {
|
||||
Log.error("WARNING: " + message);
|
||||
}
|
||||
}, saxFactory);
|
||||
|
||||
ValidatorErrorHandler validatorErrorHandler = new ValidatorErrorHandler();
|
||||
Verifier verifier = new Verifier(docDeclaration, validatorErrorHandler);
|
||||
|
||||
SAXWriter writer = new SAXWriter((ContentHandler) verifier);
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class ValidatorErrorHandler implements ErrorHandler {
|
||||
public void error(SAXParseException e) {
|
||||
Log.error("ERROR:" + e);
|
||||
}
|
||||
|
||||
public void fatalError(SAXParseException e) {
|
||||
Log.error("Fatal:" + e);
|
||||
}
|
||||
|
||||
public void warning(SAXParseException e) {
|
||||
Log.error("Warning:" + e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,326 +0,0 @@
|
|||
package org.jivesoftware.openfire;
|
||||
|
||||
/**
|
||||
* $RCSfile$
|
||||
* $Revision: 1759 $
|
||||
* $Date: 2005-08-09 19:32:51 -0300 (Tue, 09 Aug 2005) $
|
||||
*
|
||||
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
|
||||
*
|
||||
* This software is published under the terms of the GNU Public License (GPL),
|
||||
* a copy of which is included in this distribution, or a commercial license
|
||||
* agreement with Jive.
|
||||
*/
|
||||
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentHelper;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.Namespace;
|
||||
import org.dom4j.QName;
|
||||
import org.dom4j.io.SAXReader;
|
||||
import org.jivesoftware.database.DbConnectionManager;
|
||||
import org.jivesoftware.util.JiveGlobals;
|
||||
import org.jivesoftware.util.LocaleUtils;
|
||||
import org.jivesoftware.util.Log;
|
||||
import org.jivesoftware.openfire.container.BasicModule;
|
||||
import org.jivesoftware.openfire.event.UserEventDispatcher;
|
||||
import org.jivesoftware.openfire.event.UserEventListener;
|
||||
import org.jivesoftware.openfire.user.User;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* Private storage for user accounts (JEP-0049). It is used by some XMPP systems
|
||||
* for saving client settings on the server.
|
||||
*
|
||||
* @author Iain Shigeoka
|
||||
*/
|
||||
public class PrivateStorage extends BasicModule implements UserEventListener {
|
||||
|
||||
private static final String LOAD_PRIVATE = "SELECT privateData FROM ofPrivate WHERE username=? AND namespace=?";
|
||||
private static final String LOAD_ALL_PRIVATE = "SELECT privateData FROM ofPrivate WHERE username=?";
|
||||
private static final String INSERT_PRIVATE = "INSERT INTO ofPrivate (privateData,name,username,namespace) VALUES (?,?,?,?)";
|
||||
private static final String UPDATE_PRIVATE = "UPDATE ofPrivate SET privateData=?, name=? WHERE username=? AND namespace=?";
|
||||
private static final String DELETE_PRIVATES = "DELETE FROM ofPrivate WHERE username=?";
|
||||
|
||||
// Currently no delete supported, we can detect an add of an empty element
|
||||
// and
|
||||
// use that to signal a delete but that optimization doesn't seem necessary.
|
||||
// private static final String DELETE_PRIVATE =
|
||||
// "DELETE FROM ofPrivate WHERE userID=? AND name=? AND namespace=?";
|
||||
|
||||
private boolean enabled = JiveGlobals.getBooleanProperty(
|
||||
"xmpp.privateStorageEnabled", true);
|
||||
|
||||
/**
|
||||
* Pool of SAX Readers. SAXReader is not thread safe so we need to have a
|
||||
* pool of readers.
|
||||
*/
|
||||
private BlockingQueue<SAXReader> xmlReaders = new LinkedBlockingQueue<SAXReader>();
|
||||
|
||||
/**
|
||||
* Constructs a new PrivateStore instance.
|
||||
*/
|
||||
public PrivateStorage() {
|
||||
super("Private user data storage");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if private storage is enabled.
|
||||
*
|
||||
* @return true if private storage is enabled.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether private storage is enabled.
|
||||
*
|
||||
* @param enabled
|
||||
* true if this private store is enabled.
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
JiveGlobals.setProperty("xmpp.privateStorageEnabled", Boolean
|
||||
.toString(enabled));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores private data. If the name and namespace of the element matches
|
||||
* another stored private data XML document, then replace it with the new
|
||||
* one.
|
||||
*
|
||||
* @param data
|
||||
* the data to store (XML element)
|
||||
* @param username
|
||||
* the username of the account where private data is being stored
|
||||
*/
|
||||
public void add(String username, Element data) {
|
||||
if (enabled) {
|
||||
java.sql.Connection con = null;
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
StringWriter writer = new StringWriter();
|
||||
data.write(writer);
|
||||
con = DbConnectionManager.getConnection();
|
||||
pstmt = con.prepareStatement(LOAD_PRIVATE);
|
||||
pstmt.setString(1, username);
|
||||
pstmt.setString(2, data.getNamespaceURI());
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
boolean update = false;
|
||||
if (rs.next()) {
|
||||
update = true;
|
||||
}
|
||||
rs.close();
|
||||
pstmt.close();
|
||||
|
||||
if (update) {
|
||||
pstmt = con.prepareStatement(UPDATE_PRIVATE);
|
||||
} else {
|
||||
pstmt = con.prepareStatement(INSERT_PRIVATE);
|
||||
}
|
||||
pstmt.setString(1, writer.toString());
|
||||
pstmt.setString(2, data.getName());
|
||||
pstmt.setString(3, username);
|
||||
pstmt.setString(4, data.getNamespaceURI());
|
||||
pstmt.executeUpdate();
|
||||
} catch (Exception e) {
|
||||
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
|
||||
} finally {
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
}
|
||||
try {
|
||||
if (con != null) {
|
||||
con.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data stored under a key corresponding to the name and
|
||||
* namespace of the given element. The Element must be in the form:
|
||||
* <p>
|
||||
*
|
||||
* <code><name xmlns='namespace'/></code>
|
||||
* <p>
|
||||
*
|
||||
* If no data is currently stored under the given key, an empty element will
|
||||
* be returned.
|
||||
*
|
||||
* @param data
|
||||
* an XML document who's element name and namespace is used to
|
||||
* match previously stored private data.
|
||||
* @param username
|
||||
* the username of the account where private data is being
|
||||
* stored.
|
||||
* @return the data stored under the given key or the data element.
|
||||
*/
|
||||
public Element get(String username, Element data) {
|
||||
if (enabled) {
|
||||
Connection con = null;
|
||||
PreparedStatement pstmt = null;
|
||||
SAXReader xmlReader = null;
|
||||
try {
|
||||
// Get a sax reader from the pool
|
||||
xmlReader = xmlReaders.take();
|
||||
con = DbConnectionManager.getConnection();
|
||||
pstmt = con.prepareStatement(LOAD_PRIVATE);
|
||||
pstmt.setString(1, username);
|
||||
pstmt.setString(2, data.getNamespaceURI());
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
data.clearContent();
|
||||
String result = rs.getString(1).trim();
|
||||
Document doc = xmlReader.read(new StringReader(result));
|
||||
data = doc.getRootElement();
|
||||
}
|
||||
rs.close();
|
||||
} catch (Exception e) {
|
||||
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
|
||||
} finally {
|
||||
// Return the sax reader to the pool
|
||||
if (xmlReader != null) {
|
||||
xmlReaders.add(xmlReader);
|
||||
}
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
}
|
||||
try {
|
||||
if (con != null) {
|
||||
con.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public Element getAll(String username) {
|
||||
QName qName = new QName("", new Namespace("","jabber:iq:private"), "query");
|
||||
Element data = DocumentHelper.createElement(qName);
|
||||
|
||||
if (enabled) {
|
||||
Connection con = null;
|
||||
PreparedStatement pstmt = null;
|
||||
SAXReader xmlReader = null;
|
||||
try {
|
||||
// Get a sax reader from the pool
|
||||
xmlReader = xmlReaders.take();
|
||||
con = DbConnectionManager.getConnection();
|
||||
pstmt = con.prepareStatement(LOAD_ALL_PRIVATE);
|
||||
pstmt.setString(1, username);
|
||||
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
String result = rs.getString(1).trim();
|
||||
Document doc = xmlReader.read(new StringReader(result));
|
||||
data.add(doc.getRootElement());
|
||||
}
|
||||
rs.close();
|
||||
} catch (Exception e) {
|
||||
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
|
||||
} finally {
|
||||
// Return the sax reader to the pool
|
||||
if (xmlReader != null) {
|
||||
xmlReaders.add(xmlReader);
|
||||
}
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
}
|
||||
try {
|
||||
if (con != null) {
|
||||
con.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public void userCreated(User user, Map params) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void userDeleting(User user, Map params) {
|
||||
// Delete all private properties of the user
|
||||
java.sql.Connection con = null;
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
con = DbConnectionManager.getConnection();
|
||||
pstmt = con.prepareStatement(DELETE_PRIVATES);
|
||||
pstmt.setString(1, user.getUsername());
|
||||
pstmt.executeUpdate();
|
||||
} catch (Exception e) {
|
||||
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
|
||||
} finally {
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
}
|
||||
try {
|
||||
if (con != null) {
|
||||
con.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void userModified(User user, Map params) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void start() throws IllegalStateException {
|
||||
super.start();
|
||||
// Initialize the pool of sax readers
|
||||
for (int i = 0; i < 10; i++) {
|
||||
SAXReader xmlReader = new SAXReader();
|
||||
xmlReader.setEncoding("UTF-8");
|
||||
xmlReaders.add(xmlReader);
|
||||
}
|
||||
// Add this module as a user event listener so we can delete
|
||||
// all user properties when a user is deleted
|
||||
UserEventDispatcher.addListener(this);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
super.stop();
|
||||
// Clean up the pool of sax readers
|
||||
xmlReaders.clear();
|
||||
// Remove this module as a user event listener
|
||||
UserEventDispatcher.removeListener(this);
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<%@ page import="java.io.OutputStream,
|
||||
org.jivesoftware.openfire.XMPPServer,
|
||||
net.processone.plugin.openfire.ExporterXEP227"
|
||||
contentType="application/x-download" %>
|
||||
|
||||
<!--NOTE: This JSP file should be implemented with JSTL and Servlet,
|
||||
Is not a good idea to call your self pages and add scriplet to handle -->
|
||||
|
||||
<%String fileName = request.getParameter("fileName");
|
||||
response.setContentType("application/x-download");
|
||||
response.setHeader("Content-Disposition","attachment;filename="+fileName+".xml");
|
||||
ExporterXEP227 plugin = (ExporterXEP227) XMPPServer.getInstance().getPluginManager().getPlugin("openfireexporter");
|
||||
byte[] content = plugin.exportUsersToByteArray();
|
||||
OutputStream os = response.getOutputStream();
|
||||
os.write(content);
|
||||
os.flush();
|
||||
os.close();%>
|
|
@ -1,137 +0,0 @@
|
|||
<%@ page
|
||||
import="java.io.IOException,java.util.*,
|
||||
net.processone.plugin.openfire.ExporterXEP227,
|
||||
org.jivesoftware.openfire.XMPPServer,
|
||||
org.jivesoftware.util.ParamUtils"%>
|
||||
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
|
||||
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt"%>
|
||||
|
||||
<!--NOTE: This JSP file should be implemented with JSTL and Servlet,
|
||||
Is not a good idea to call your self pages and add scriplet to handle -->
|
||||
|
||||
<%
|
||||
boolean exportUsers = request.getParameter("exportUsers") != null;
|
||||
boolean success = request.getParameter("success") != null;
|
||||
boolean exportToFile = ParamUtils.getBooleanParameter(request,
|
||||
"exporttofile", true);
|
||||
|
||||
ExporterXEP227 plugin = (ExporterXEP227) XMPPServer.getInstance()
|
||||
.getPluginManager().getPlugin("openfireexporter");
|
||||
|
||||
String exportText = "";
|
||||
|
||||
Map<String, String> errors = new HashMap<String, String>();
|
||||
if (exportUsers) {
|
||||
if (exportToFile) {
|
||||
String file = ParamUtils
|
||||
.getParameter(request, "exportFile");
|
||||
if ((file == null) || (file.length() <= 0)) {
|
||||
errors.put("missingFile", "missingFile");
|
||||
} else {
|
||||
response.sendRedirect("export-file.jsp?fileName="
|
||||
+ file);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
exportText = plugin.exportUsersToString();
|
||||
} catch (IOException e) {
|
||||
errors.put("IOException", "IOException");
|
||||
}
|
||||
}
|
||||
}
|
||||
%>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Export User Data</title>
|
||||
<meta name="pageID" content="import-export-selection" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<%
|
||||
if (errors.size() > 0) {
|
||||
%>
|
||||
|
||||
<div class="jive-error">
|
||||
<table cellpadding="0" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="jive-icon"><img src="images/error-16x16.gif"
|
||||
width="16" height="16" border="0"></td>
|
||||
<td class="jive-icon-label">
|
||||
<%
|
||||
if (errors.containsKey("missingFile")) {
|
||||
%> Missing or bad file
|
||||
name. <%
|
||||
} else if (errors.containsKey("IOException")
|
||||
|| errors.containsKey("fileNotCreated")) {
|
||||
%>
|
||||
Couldn't create export file. <%
|
||||
}
|
||||
%>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<%
|
||||
} else if (ParamUtils.getBooleanParameter(request, "success")) {
|
||||
%>
|
||||
|
||||
<div class="jive-success">
|
||||
<table cellpadding="0" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="jive-icon"><img src="images/success-16x16.gif"
|
||||
width="16" height="16" border="0"></td>
|
||||
<td class="jive-icon-label">User data successfully exported.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
<form action="export-user-data.jsp?exportUsers" method="post">
|
||||
|
||||
<div class="jive-contentBoxHeader">Export Options</div>
|
||||
<div class="jive-contentBox">
|
||||
<p>Select the radio button next to the desired export option and
|
||||
then click on the Export button.</p>
|
||||
|
||||
<table cellpadding="3" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="1%"> </td>
|
||||
<td width="99%">Export File Name: <input type="text"
|
||||
size="30" maxlength="150" name="exportFile"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="1%"> </td>
|
||||
<td width="99%"><textarea cols="80" rows="20" wrap=off><%=exportText%></textarea></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Export"></form>
|
||||
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 594 B |
Binary file not shown.
Before Width: | Height: | Size: 1016 B |
|
@ -1,42 +0,0 @@
|
|||
<%@ page import="net.processone.plugin.openfire.ExporterXEP227,
|
||||
org.jivesoftware.openfire.XMPPServer"
|
||||
%>
|
||||
|
||||
<!--NOTE: This JSP file should be implemented with JSTL and Servlet,
|
||||
Is not a good idea to call your self pages and add scriplet to handle -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Import/Export Selection</title>
|
||||
<meta name="pageID" content="import-export-selection"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<jsp:useBean id="pageinfo" scope="request" class="org.jivesoftware.admin.AdminPageBean" />
|
||||
<%
|
||||
ExporterXEP227 plugin = (ExporterXEP227) XMPPServer.getInstance().getPluginManager().getPlugin("openfireexporter");
|
||||
%>
|
||||
<p>
|
||||
Openfire plugin improved to export to XEP-227 format.
|
||||
There is also old code to import, but using a custom Openfire format.
|
||||
If you are interested you can improve it to also support XEP-227 import.
|
||||
|
||||
<ul>
|
||||
<li><a href="import-user-data.jsp">Import User Data</a></li>
|
||||
<li><a href="export-user-data.jsp">Export User Data</a></li>
|
||||
</ul>
|
||||
|
||||
<%
|
||||
if (plugin.isUserProviderReadOnly()) {
|
||||
%>
|
||||
|
||||
Note: because you are using a read-only user data store such as LDAP or POP3 you will only be able to import user roster data, not users themselves.
|
||||
Please see the <a href="../../plugin-admin.jsp?plugin=openfireexporter&showReadme=true&decorator=none">readme</a> for details.
|
||||
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,168 +0,0 @@
|
|||
<%@ page import="java.net.MalformedURLException,
|
||||
java.util.*,
|
||||
org.dom4j.DocumentException,
|
||||
org.apache.commons.fileupload.DiskFileUpload,
|
||||
org.apache.commons.fileupload.FileItem,net.processone.plugin.openfire.ExporterXEP227,
|
||||
org.jivesoftware.openfire.XMPPServer,
|
||||
org.jivesoftware.util.ParamUtils"
|
||||
%>
|
||||
|
||||
<!--NOTE: This JSP file should be implemented with JSTL and Servlet,
|
||||
Is not a good idea to call your self pages and add scriplet to handle -->
|
||||
|
||||
<%
|
||||
boolean importUsers = request.getParameter("importUsers") != null;
|
||||
|
||||
ExporterXEP227 plugin = (ExporterXEP227) XMPPServer.getInstance().getPluginManager().getPlugin("openfireexporter");
|
||||
List<String> duplicateUsers = new ArrayList<String>();
|
||||
|
||||
Map<String, String> errors = new HashMap<String, String>();
|
||||
if (importUsers) {
|
||||
DiskFileUpload dfu = new DiskFileUpload();
|
||||
|
||||
List fileItems = dfu.parseRequest(request);
|
||||
Iterator i = fileItems.iterator();
|
||||
FileItem fi = (FileItem) i.next();
|
||||
FileItem pd = (FileItem) i.next();
|
||||
String previousDomain = pd.getString();
|
||||
|
||||
if (plugin.validateImportFile(fi)) {
|
||||
try {
|
||||
if (isEmpty(previousDomain)) {
|
||||
duplicateUsers.addAll(plugin.importUserData(fi, null));
|
||||
}
|
||||
else if (!isEmpty(previousDomain)) {
|
||||
duplicateUsers.addAll(plugin.importUserData(fi, previousDomain));
|
||||
}
|
||||
else {
|
||||
errors.put("missingDomain", "missingDomain");
|
||||
}
|
||||
|
||||
if (duplicateUsers.size() == 0) {
|
||||
response.sendRedirect("import-user-data.jsp?success=true");
|
||||
return;
|
||||
}
|
||||
|
||||
errors.put("invalidUser", "invalidUser");
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
errors.put("IOException", "IOException");
|
||||
}
|
||||
catch (DocumentException e) {
|
||||
errors.put("DocumentException", "DocumentException");
|
||||
}
|
||||
}
|
||||
else {
|
||||
errors.put("invalidUserFile", "invalidUserFile");
|
||||
}
|
||||
}
|
||||
%>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Import User Data</title>
|
||||
<meta name="pageID" content="import-export-selection"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<% if (errors.size() > 0) { %>
|
||||
|
||||
<div class="jive-error">
|
||||
<table cellpadding="0" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="jive-icon"><img src="images/error-16x16.gif" width="16" height="16" border="0"></td>
|
||||
<td class="jive-icon-label">
|
||||
<% if (errors.containsKey("missingDomain")) { %>
|
||||
You must supply both a existing and new domain name.
|
||||
<% } else if (errors.containsKey("IOException")) { %>
|
||||
Missing or bad file name.
|
||||
<% } else if (errors.containsKey("DocumentException")) { %>
|
||||
Import failed.
|
||||
<% } else if (errors.containsKey("invalidUserFile")) { %>
|
||||
The import file does not match the user schema.
|
||||
<% } else if (errors.containsKey("invalidUser")) { %>
|
||||
|
||||
<% if (plugin.isUserProviderReadOnly()) { %>
|
||||
The following users did not exist in the system or have invalid username so their roster was not loaded:<br>
|
||||
<% } else { %>
|
||||
The following users already exist in the system or have invalid username and were not loaded:<br>
|
||||
<% } %>
|
||||
<%
|
||||
Iterator iter = duplicateUsers.iterator();
|
||||
while (iter.hasNext()) {
|
||||
String username = (String) iter.next();
|
||||
%><%= username %><%
|
||||
if (iter.hasNext()) {
|
||||
%>, <%
|
||||
} else {
|
||||
%>.<%
|
||||
}
|
||||
}
|
||||
} %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<% } else if (ParamUtils.getBooleanParameter(request, "success")) { %>
|
||||
|
||||
<div class="jive-success">
|
||||
<table cellpadding="0" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="jive-icon"><img src="images/success-16x16.gif" width="16" height="16" border="0"></td>
|
||||
<% if (plugin.isUserProviderReadOnly()) { %>
|
||||
<td class="jive-icon-label">User roster data added successfully.</td>
|
||||
<% } else { %>
|
||||
<td class="jive-icon-label">All users added successfully.</td>
|
||||
<% } %>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<% } %>
|
||||
|
||||
Use the form below to import a user data XML file.
|
||||
|
||||
|
||||
<form action="import-user-data.jsp?importUsers" method="post" enctype="multipart/form-data">
|
||||
|
||||
<div class="jive-contentBoxHeader">Import</div>
|
||||
<div class="jive-contentBox">
|
||||
<p>
|
||||
Choose a file to import:</p>
|
||||
<input type="file" name="thefile">
|
||||
|
||||
<br><br><br>
|
||||
|
||||
<p>
|
||||
<b>Optional</b> - Use the field below to replace the domain name of user roster entries with the current hostname.
|
||||
See the migration section of the <a href="../../plugin-admin.jsp?plugin=openfireexporter&showReadme=true&decorator=none">readme</a> for details.
|
||||
</p>
|
||||
Replace Domain: <input type="text" size="20" maxlength="150" name="previousDomain" value=""/>
|
||||
</div>
|
||||
<input type="submit" value="Import">
|
||||
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<%!
|
||||
public boolean isEmpty(String s) {
|
||||
if (s == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s.trim().length() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
%>
|
Loading…
Reference in New Issue