Commit 61534da4 authored by Dave Cridland's avatar Dave Cridland Committed by GitHub

Merge pull request #647 from annovanvliet/UserImExportXepp0227

OF-1197: XEP-0227 support for User import Export plugin
parents 7e2dd8e2 2d78ee96
......@@ -120,10 +120,6 @@
<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/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/smackx.jar"/>
<classpathentry kind="lib" path="src/web/WEB-INF/lib/commons-fileupload.jar"/>
......
......@@ -148,6 +148,11 @@ hr {
</div>
<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>
<ul>
<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"?>
<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:complexType>
<xs:sequence>
......@@ -11,8 +11,8 @@
<xs:element name="User">
<xs:complexType>
<xs:all>
<xs:element ref="Username" use="required" />
<xs:element ref="Password" use="required" />
<xs:element ref="Username" />
<xs:element ref="Password" />
<xs:element ref="Email" />
<xs:element ref="Name" />
<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 @@
<name>User Import Export</name>
<description>Enables import and export of user data</description>
<author>Ryan Graham</author>
<version>2.5.0</version>
<date>10/12/2015</date>
<version>2.6.0</version>
<date>09/26/2016</date>
<minServerVersion>4.0.0</minServerVersion>
<adminconsole>
......
......@@ -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>
<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
or POP3 this plugin will still work with two caveats:
<ol>
<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.
</ol>
<li>When importing, no new users will be created but if the user exists in the user store the roster will be loaded.
</ol>
<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
......@@ -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
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>
<br>
<br/>
<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
......@@ -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
the "To Screen" radio button and then click on the "Export" button. The user data will be placed in
the provided text area.</li>
<br>
<br/>
<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
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
......@@ -406,6 +407,15 @@ Below is a list of the different status types and what their associated numbers
</table>
</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>
......
/**
*
* Copyright 2016 Ryan Graham
*
* 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 gnu.inet.encoding.Stringprep;
import gnu.inet.encoding.StringprepException;
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.apache.commons.lang.StringEscapeUtils;
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.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.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.jivesoftware.openfire.user.UserProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
/**
* The user import/export plugin provides a way to import and export Openfire
......@@ -49,23 +48,17 @@ public class ImportExportPlugin implements Plugin {
private static final Logger Log = LoggerFactory.getLogger(ImportExportPlugin.class);
private UserManager userManager;
private UserProvider provider;
private String serverName;
public ImportExportPlugin() {
userManager = XMPPServer.getInstance().getUserManager();
provider = UserManager.getUserProvider();
serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
}
public void initializePlugin(PluginManager manager, File pluginDirectory) {
}
public void destroyPlugin() {
userManager = null;
provider = null;
serverName = null;
}
/**
......@@ -84,11 +77,11 @@ public class ImportExportPlugin implements Plugin {
* @return a byte[] of the user data.
* @throws IOException if there's a problem writing to the XMLWriter.
*/
public byte[] exportUsersToByteArray() throws IOException {
public byte[] exportUsersToByteArray(boolean xep227Support) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
writer.write(exportUsers());
writer.write(exportUsers(xep227Support));
return out.toByteArray();
}
......@@ -100,12 +93,12 @@ public class ImportExportPlugin implements Plugin {
* @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 {
public String exportUsersToString(boolean xep227Support) throws IOException {
StringWriter stringWriter = new StringWriter();
XMLWriter writer = null;
try {
writer = new XMLWriter(stringWriter, OutputFormat.createPrettyPrint());
writer.write(exportUsers());
writer.write(exportUsers(xep227Support));
} catch (IOException ioe) {
Log.error(ioe.getMessage(), ioe);
throw ioe;
......@@ -114,8 +107,8 @@ public class ImportExportPlugin implements Plugin {
writer.close();
}
}
return stringWriter.toString();
return StringEscapeUtils.escapeHtml(stringWriter.toString());
}
/**
......@@ -132,10 +125,11 @@ public class ImportExportPlugin implements Plugin {
* @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);
public List<String> importUserData(FileItem file, String previousDomain, boolean xep227Support) throws DocumentException, IOException {
InExporter exporter = XMLImportExportFactory.getExportInstance(xep227Support);
return exporter.importUsers(file.getInputStream(), previousDomain,isUserProviderReadOnly());
}
/**
......@@ -144,9 +138,11 @@ public class ImportExportPlugin implements Plugin {
* @param file a FileItem to be validated.
* @return True if FileItem matches the openfire user schema.
*/
public boolean validateImportFile(FileItem file) {
public boolean validateImportFile(FileItem usersFile, boolean xep227Support) {
try {
return new UserSchemaValidator(file, "wildfire-user-schema.xsd.xml").validate();
InExporter exporter = XMLImportExportFactory.getExportInstance(xep227Support);
return exporter.validate(usersFile.getInputStream());
}
catch (Exception e) {
Log.error(e.getMessage(), e);
......@@ -154,160 +150,11 @@ public class ImportExportPlugin implements Plugin {
}
}
private Document 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 {
userElement.addElement("Password").addText(AuthFactory.getPassword(user.getUsername()));
}
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 datte 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;
private Document exportUsers(boolean xep227Support) {
InExporter exporter = XMLImportExportFactory.getExportInstance(xep227Support);
return exporter.exportUsers();
}
private List<String> importUsers(Document document, String previousDomain) {
List<String> invalidUsers = new ArrayList<String>();
UserManager userManager = UserManager.getInstance();
RosterItemProvider rosterItemProvider = RosterManager.getRosterItemProvider();
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;
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;
import java.io.IOException;
import java.io.InputStream;
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.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.Locator;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
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;
/**
* An utility to convert a file to XML, and validate the XML against a set of XML Schema files.
*
* @author Anno van Vliet
*
*/
public class UserSchemaValidator {
private static final Logger Log = LoggerFactory.getLogger(UserSchemaValidator.class);
private Document doc;
private String schema;
private static final Logger Log = LoggerFactory.getLogger(UserSchemaValidator.class);
private static final CharSequence STRICT_DECLARATION_MSG = "The matching wildcard is strict, but no declaration can be found for element";
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 {
SAXReader reader = new SAXReader();
doc = reader.read(usersFile.getInputStream());
schemaSources = new Source[sourceList.size()];
sourceList.toArray(schemaSources);
}
/**
* 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 = schemaURL.toExternalForm();
}
Schema schema = schemaFactory.newSchema(schemaSources);
documentBuilderFactory.setSchema(schema);
Log.info("Start validating document");
boolean validate() {
try {
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
saxFactory.setNamespaceAware(true);
DocumentDeclaration docDeclaration = GrammarLoader.loadVGM(schema, new IgnoreController() {
@Override
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);
} else {
Log.info("Loading document");
}
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
handler.reset();
// Communicate some info about the Xincludes in the imported file. These imports should be resolvable by the server.
documentBuilder.setEntityResolver( new EntityResolver() {
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.getMessage(), e);
return false;
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
Log.info(String.format("resolved Entity:%s %s", (publicId != null ? publicId : "") , systemId));
return null;
}
} );
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) {
Log.error("Fatal:" + e);
}
private class ValidatorErrorHandler implements ErrorHandler {
private int nrOfErrors = 0;
public void warning(SAXParseException e) {
Log.error("Warning:" + e);
}
public void error(SAXParseException 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();
}
}
}
/**
*
* 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.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.DOMReader;
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.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.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.jivesoftware.openfire.vcard.VCardManager;
import org.jivesoftware.util.XMPPDateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
import gnu.inet.encoding.Stringprep;
import gnu.inet.encoding.StringprepException;
/**
* A In and Exporter which conforms to XEP-0227.
*
* @author Anno van Vliet
*
*/
public class Xep227Exporter implements InExporter {
/**
* constants defining field and attribute names
*/
private static final String V_CARD_NAME = "vCard";
private static final String ASK_SUBSCRIBE_ENUM = "subscribe";
private static final String STAMP_NAME = "stamp";
private static final String DELAY_ELEMENT_NAME = "delay";
private static final String FROM_NAME = "from";
private static final String MESSAGE_ELEMENT_NAME = "message";
private static final String OFFLINE_MESSAGES_ELEMENT_NAME = "offline-messages";
private static final String GROUP_ELEMENT_NAME = "group";
private static final String SUBSCRIPTION_NAME = "subscription";
private static final String ASK_NAME = "ask";
private static final String ITEM_ELEMENT_NAME = "item";
private static final String QUERY_ELEMENT_NAME = "query";
private static final String PASSWORD_NAME = "password";
private static final String NAME_NAME = "name";
private static final String USER_ELEMENT_NAME = "user";
private static final String JID_NAME = "jid";
private static final String HOST_ELEMENT_NAME = "host";
private static final String SERVER_DATA_ELEMENT_NAME = "server-data";
/**
* the relevant namespaces
*/
private static final String JABBER_CLIENT_NS = "jabber:client";
private static final String VCARD_TEMP_NS = "vcard-temp";
private static final String JABBER_IQ_ROSTER_NS = "jabber:iq:roster";
private static final String URN_XMPP_PIE_0_NS = "urn:xmpp:pie:0";
private static final Namespace JABBER_MSG_NS = new Namespace("", JABBER_CLIENT_NS);
private static final Logger Log = LoggerFactory.getLogger(Xep227Exporter.class);
private final String serverName;
private final OfflineMessageStore offlineMessagesStore;
private final VCardManager vCardManager;
//private final PrivateStorage privateStorage;
private final UserManager userManager;
private final RosterItemProvider rosterItemProvider;
private final DateFormat dateformater = new SimpleDateFormat(XMPPDateTimeFormat.XMPP_DATETIME_FORMAT, Locale.US);
/**
*
*/
public Xep227Exporter() {
offlineMessagesStore = XMPPServer.getInstance()
.getOfflineMessageStore();
serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
//TODO not yet implemented
//privateStorage = XMPPServer.getInstance().getPrivateStorage();
vCardManager = VCardManager.getInstance();
userManager = UserManager.getInstance();
rosterItemProvider = RosterManager.getRosterItemProvider();
}
/**
* Constructor used for testing purposes.
*
* @param serverName
* @param offlineMessagesStore
* @param vCardManager
* @param privateStorage
* @param userManager
* @param rosterItemProvider
*/
public Xep227Exporter(String serverName, OfflineMessageStore offlineMessagesStore, VCardManager vCardManager, PrivateStorage privateStorage,
UserManager userManager, RosterItemProvider rosterItemProvider) {
super();
this.serverName = serverName;
this.offlineMessagesStore = offlineMessagesStore;
this.vCardManager = vCardManager;
//this.privateStorage = privateStorage;
this.userManager = userManager;
this.rosterItemProvider = rosterItemProvider;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.plugin.Exporter#exportUsers(org.jivesoftware.openfire.user.UserManager)
*/
@Override
public Document exportUsers() {
Log.debug("exportUsers");
Document document = DocumentHelper.createDocument();
Element root = document.addElement(SERVER_DATA_ELEMENT_NAME, URN_XMPP_PIE_0_NS);
Element host = root.addElement(HOST_ELEMENT_NAME);
host.addAttribute(JID_NAME, serverName);
Collection<User> users = userManager.getUsers();
for (User user : users) {
String userName = user.getUsername();
Element userElement = host.addElement(USER_ELEMENT_NAME);
exportUser(userElement, user);
exportOfflineMessages(serverName, userElement, userName);
exportRoster(userElement, user);
exportVCard(userElement, userName);
exportPrivateStorage(userName, userElement);
}
return document;
}
/**
* Adding heading of an user and its parameters
*
* @param userElement
* DOM element
* @param user
* User object
*/
private void exportUser(Element userElement, User user) {
String userName = user.getUsername();
userElement.addAttribute(NAME_NAME, userName);
try {
String pw = AuthFactory.getPassword(userName);
userElement.addAttribute(PASSWORD_NAME, pw );
} catch (UserNotFoundException e) {
Log.info("User " + userName
+ " not found, setting their password to their username");
userElement.addAttribute(PASSWORD_NAME, userName);
} catch (UnsupportedOperationException e) {
Log.info("Unable to retrieve " + userName
+ " password, setting their password to their username");
userElement.addAttribute(PASSWORD_NAME, userName);
}
}
/**
* Add roster and its groups to a DOM element
*
* @param userElement
* DOM element
* @param user
* User
*/
private void exportRoster(Element userElement, User user) {
Element rosterElement = userElement.addElement(QUERY_ELEMENT_NAME,
JABBER_IQ_ROSTER_NS);
Collection<RosterItem> roster = user.getRoster().getRosterItems();
for (RosterItem ri : roster) {
Element itemElement = rosterElement.addElement(ITEM_ELEMENT_NAME);
itemElement.addAttribute(JID_NAME, ri.getJid().toBareJID());
itemElement.addAttribute(NAME_NAME, ri.getNickname());
itemElement.addAttribute(SUBSCRIPTION_NAME, ri.getSubStatus()
.getName());
if ( ri.getAskStatus() == AskType.SUBSCRIBE ) {
itemElement.addAttribute(ASK_NAME, ASK_SUBSCRIBE_ENUM);
}
/**
* Adding groups
*/
Element groupElement = itemElement.addElement(GROUP_ELEMENT_NAME);
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
*/
@SuppressWarnings("unchecked")
private void exportOfflineMessages(String hostname, Element userElement,
String userName) {
Collection<OfflineMessage> offlineMessages = offlineMessagesStore
.getMessages(userName, false);
if (!offlineMessages.isEmpty()) {
Element offlineElement = userElement.addElement(OFFLINE_MESSAGES_ELEMENT_NAME);
for (OfflineMessage offMessage : offlineMessages) {
Element messageElement = offlineElement.addElement(new QName(MESSAGE_ELEMENT_NAME, JABBER_MSG_NS));
for ( Object att : offMessage.getElement().attributes() ) {
Attribute attribute = (Attribute) att;
messageElement.addAttribute(attribute.getQName(),attribute.getValue());
}
for (Iterator<Element> iterator = offMessage.getElement().elementIterator(); iterator
.hasNext();) {
Element element = iterator.next();
messageElement.add(element.createCopy(new QName(element.getName(), (element.getNamespace() == Namespace.NO_NAMESPACE ? JABBER_MSG_NS : element.getNamespace()))));
}
/**
* Adding delay element
*/
Element delayElement = messageElement.addElement("delay", "urn:xmpp:delay");
delayElement.addAttribute(FROM_NAME, hostname);
delayElement.addAttribute("stamp", XMPPDateTimeFormat.format(offMessage.getCreationDate()));
delayElement.addText("Offline Storage");
}
}
}
/**
* Adding vcard element
*
* @param userElement
* DOM element
* @param userName
* user name
*/
@SuppressWarnings("unchecked")
private void exportVCard(Element userElement, String userName) {
Element vCard = vCardManager.getVCard(userName);
if (vCard != null) {
Element vCardElement = userElement
.addElement(V_CARD_NAME, VCARD_TEMP_NS);
for (Iterator<Element> iterator = vCard.elementIterator(); iterator
.hasNext();) {
Element element = iterator.next();
vCardElement.add(element.createCopy());
}
}
}
/**
* Add all the private stored information (XEP-0049)
* <b>Note: this method is not supported in the available openfire releases.
*
* </b>
* @param userName User name
* @param userElement User element
*/
private void exportPrivateStorage(String userName, Element userElement) {
// Element result = privateStorage.getAll(userName);
// if (result.elements().size() > 0) {
// userElement.add(result.createCopy());
// }
}
/* (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, "pie.xsd", "jabber-iq-roster.xsd", "jabber-iq-private.xsd","xml.xsd","stanzaerror.xsd","jabber-client.xsd").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, boolean)
*/
@SuppressWarnings("unchecked")
private List<String> importUsers(Document document, String previousDomain, boolean isUserProviderReadOnly) {
List<String> invalidUsers = new ArrayList<String>();
Element hosts = document.getRootElement();
Iterator<Element> hostsIter = hosts.elementIterator(HOST_ELEMENT_NAME);
while (hostsIter.hasNext()) {
Element host = hostsIter.next();
Iterator<Element> usersIter = host.elementIterator(USER_ELEMENT_NAME);
while (usersIter.hasNext()) {
Element user = usersIter.next();
importUser(user,previousDomain,isUserProviderReadOnly,invalidUsers);
}
}
return invalidUsers;
}
/**
* @param user
* @param previousDomain
* @param isUserProviderReadOnly
* @param invalidUsers
*/
@SuppressWarnings("unchecked")
private void importUser(Element user, String previousDomain, boolean isUserProviderReadOnly, List<String> invalidUsers) {
Log.debug("importUser");
List<RosterItem> rosterItems = new ArrayList<RosterItem>();
List<OfflineMessage> offlineMessages = new ArrayList<OfflineMessage>();
Element vCardElement = null;
String userName = user.attributeValue(NAME_NAME);
String password = user.attributeValue(PASSWORD_NAME);
Iterator<Element> userElements = user.elementIterator();
while (userElements.hasNext()) {
Element userElement = userElements.next();
String nameElement = userElement.getName();
if (OFFLINE_MESSAGES_ELEMENT_NAME.equals(nameElement)) {
importOffLineMessages(userElement, offlineMessages);
} else if (QUERY_ELEMENT_NAME.equals(nameElement) && JABBER_IQ_ROSTER_NS.equals(userElement.getNamespaceURI())) {
importUserRoster(userElement, rosterItems, previousDomain);
} else if (V_CARD_NAME.equals(nameElement) && VCARD_TEMP_NS.equals(userElement.getNamespaceURI())) {
vCardElement = userElement;
}
}
if ( userName != null ) {
try {
userName = Stringprep.nodeprep(userName);
if (!isUserProviderReadOnly && (password != null) ) {
userManager.createUser(userName, password, userName, null);
}
if ( !isUserProviderReadOnly && vCardElement != null ) {
try {
vCardManager.setVCard(userName, vCardElement);
} catch (Exception e) {
Log.warn("Error updating VCard:" + userName + ":" + e.getMessage());
Log.debug("", e);
}
}
// 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);
}
for (OfflineMessage offlineMessage : offlineMessages) {
offlineMessagesStore.addMessage(offlineMessage);
}
} 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);
} catch (Exception e) {
Log.warn("Error updating User:" + userName + ":" + e.getLocalizedMessage());
invalidUsers.add(userName);
}
}
}
/**
* @param userElement
* @param rosterItems
* @param previousDomain
*/
@SuppressWarnings("unchecked")
private void importUserRoster(Element userElement, List<RosterItem> rosterItems, String previousDomain) {
Log.debug("importUserRoster");
Iterator<Element> rosterIter = userElement.elementIterator(ITEM_ELEMENT_NAME);
while (rosterIter.hasNext()) {
Element rosterElement = rosterIter.next();
String jid = rosterElement.attributeValue(JID_NAME);
String nickname = rosterElement.attributeValue(NAME_NAME);
String substatus = rosterElement.attributeValue(SUBSCRIPTION_NAME);
String askstatus = rosterElement.attributeValue(ASK_NAME);
List<String> groups = new ArrayList<String>();
Iterator<Element> groupIter = rosterElement.elementIterator(GROUP_ELEMENT_NAME);
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 != null ) {
jid = jid.replace(previousDomain, serverName);
}
try {
rosterItems.add(new RosterItem(new JID(jid),
( substatus != null ? SubType.valueOf(substatus.toUpperCase()) : SubType.BOTH ),
( ASK_SUBSCRIBE_ENUM.equals(askstatus) ? AskType.SUBSCRIBE : AskType.NONE),
RecvType.NONE,
nickname, groups));
} catch (Exception e) {
Log.warn("Adding User Roster failed:" + e.getLocalizedMessage());
Log.debug("", e);
}
}
}
/**
* @param userElement
* @param offlineMessages
*/
@SuppressWarnings("unchecked")
private void importOffLineMessages(Element userElement, List<OfflineMessage> offlineMessages) {
Log.debug("importOffLineMessages");
// TODO Auto-generated method stub
Iterator<Element> messageIter = userElement.elementIterator(MESSAGE_ELEMENT_NAME);
while (messageIter.hasNext()) {
Element msgElement = messageIter.next();
String creationDateStr = null;
if (msgElement.element(DELAY_ELEMENT_NAME) != null) {
creationDateStr = msgElement.element(DELAY_ELEMENT_NAME).attributeValue(STAMP_NAME);
}
Date creationDate = null;
try {
if (creationDateStr != null) {
creationDate = dateformater.parse(creationDateStr);
}
} catch (ParseException e) {
Log.warn("Date not parsable:" + e.getLocalizedMessage());
}
offlineMessages.add(new OfflineMessage(creationDate, msgElement));
}
}
}
<?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"?>
<server-data xmlns="urn:xmpp:pie:0">
<host jid="openfire.xmpp.test">
<user name="admin" password="admin">
<offline-messages>
<message xmlns="jabber:client" from="openfire.xmpp.test" to="admin@openfire.xmpp.test" type="normal">
<body>A server or plugin update was found: Monitoring Service 1.5.0</body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Thu Jan 21 14:17:35 CET 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="openfire.xmpp.test" to="admin@openfire.xmpp.test" type="normal">
<body>A server or plugin update was found: Monitoring Service 1.5.0</body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Mon Jan 25 11:38:03 CET 2016">Offline Storage</delay>
</message>
</offline-messages>
<query xmlns="jabber:iq:roster"/>
</user>
<user name="anno" password="anno">
<offline-messages>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="anno@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Wed Sep 14 18:11:51 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="anno@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Wed Sep 14 18:11:51 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="anno@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 16 13:23:35 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="anno@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 16 13:23:35 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="anno@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Mon Sep 19 16:35:21 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="anno@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Mon Sep 19 16:35:21 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="anno@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Tue Sep 20 14:39:46 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="anno@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Tue Sep 20 14:39:46 CEST 2016">Offline Storage</delay>
</message>
</offline-messages>
<query xmlns="jabber:iq:roster">
<item jid="test1@openfire.xmpp.test" name="Tester Een" subscription="both">
<group/>
</item>
</query>
</user>
<user name="test1" password="test">
<offline-messages>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test1@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 16 13:23:35 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test1@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 16 13:23:35 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test1@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Mon Sep 19 16:35:21 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test1@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Mon Sep 19 16:35:21 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test1@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Tue Sep 20 14:39:46 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test1@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Tue Sep 20 14:39:46 CEST 2016">Offline Storage</delay>
</message>
</offline-messages>
<query xmlns="jabber:iq:roster">
<item jid="anno@openfire.xmpp.test" subscription="both">
<group/>
</item>
</query>
</user>
<user name="test2" password="test">
<offline-messages>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 09 17:48:00 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 09 17:48:00 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 09 17:48:00 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Wed Sep 14 11:02:37 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Wed Sep 14 11:02:37 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Wed Sep 14 11:02:37 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Wed Sep 14 18:11:51 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Wed Sep 14 18:11:51 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Wed Sep 14 18:11:51 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 16 13:23:35 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 16 13:23:35 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Fri Sep 16 13:23:35 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Mon Sep 19 16:35:21 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Mon Sep 19 16:35:21 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Mon Sep 19 16:35:21 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Tue Sep 20 14:39:46 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Tue Sep 20 14:39:46 CEST 2016">Offline Storage</delay>
</message>
<message xmlns="jabber:client" from="pubsub.openfire.xmpp.test" to="test2@openfire.xmpp.test" type="normal">
<body></body>
<delay xmlns="urn:xmpp:delay" from="openfire.xmpp.test" stamp="Tue Sep 20 14:39:46 CEST 2016">Offline Storage</delay>
</message>
</offline-messages>
<query xmlns="jabber:iq:roster"/>
<vCard xmlns="vcard-temp">
<NICKNAME>test2-nickname</NICKNAME>
<FN>My full name</FN>
<URL>www.home.net</URL>
<PHOTO>
<BINVAL>/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABLAEsDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3Siis3VNUtbKxmllnMYjIBYKeOQOwrGUlFXZnKSirsvzSrDBJKwJVFLHHXAGawLvxnp1ls8yG6O/ONqr2/wCBe9ctqd/qOrX8VxpV5cyWCqEm2zFFJBJYFSRngjtToxbrn7aiN/c3ru+v9K4KuNSdonDPFtu0NPM2W+JGjqSDbX3/AHwn/wAVUkPxB0md0Vbe9Bdgoyiev+9XJXY07LbYofvdov8A61Z0zwIHMQVWAypVcYNc39oTuc7xlVPVo9d0/VINS8zyUkXy8Z3gDrn0PtV2vBZNU1W3x9m1C7i3fe8udlz6Zwa9P0HxXpmpXzww37yssZcqUfpkDPI967qGLjU06nVh8bGp7r0Z1dFNjkSWMOhyp6GnV1naMlmigUNLIkak4y7ADNeb32qXWraxd6VcRItg87r50akEqpJUhiSOcDt3rqPGl59i0eGTZvzcKuM4/hb/AArlguy2W+zncofZ/ve/41wY2q0uVHBi53fIum5IkcWjwPbW0m9Hy5MhBOSMdselUJrhpdu7aMelRXV75k6/u8cAfe96heTpxXhTm5PyPPlNbLYinI5571SfLyGOMb5G+VUXksT0AFS3M2xc7c/N610fhrwn/aJ0/Wftvl/vg/k+Vn7r4xnPfb6VdCjKrLlijOFOVWXLEx9M8M3195v2qwvo9mNuIWGc5z1HtWVbjUvCUhv0sZUMg8nN1EwXnnjpz8v8698gt/K3fNnPtXlPxEv/ALT4fgTytuLpTndn+B/avVq4SNBJp6nZXwkKEFNPVHZeD9aXVfDtlNPLbrdyb8xI2Dw7DoST0Ga6GvIfh3qOdV02y8r/AJ6/Pu/2XPTFevV30J88EzvwtT2lJM474ksV8O25BI/0ten+49cmkkl/pcVlBIyTNGgDMcAYwT0+ldh8Q4Wn0CBUjZyLpThQT/C9cALprOENFIEmjAGDjI7Hg15+Nsqqctjgxj5a13sdDpnhS/m0m4LzWzS5YK7MxI+UY5xWJqOk3uieX9snSTzs7fLcnGMZzkD1Fdh4K1hLnTHivL2Hz3uSqozKrMCq4wO/Oa6S78P6Xqmz7ba+b5edn7xlxnr0I9BW0sFSr0U6W5t9Up1qSlS38zkvCfhOZ7kXl8LS4tZ7ffHHJlypYqQSCMZxmu+htrbTbDCW8SRwqzbYkAA6ngUsENnptvGi7IYkURpufgADgZJ9q5LxR4oFqNRt7fVLZSsJ2IGQnJTPfnqa2UaeDp+Zuo08LT13GeKvGltY/ZNn2xN+/Pl4Gcbevze9eNXuoXVxCEnup5UDZCvIWGeeeTVvUNVvNS8v7XP5nl52/KoxnGeg9hWNcP8Auxhh1ry1UniKybPAxGJliKl+h1Pw5kY+O9NXc2397xnj/VPXvVeHfDa0Y+KNMuvJfH7395g4+44+le4162HVoteZ7eXpqj/XZFHVNO/tK1WHzfL2uHztz2I9fevEdXbydav7XGfLuJE3euGIzj8K98rk/Fug3WpaJfQwyQq0rKVLkgffB5wKzxdD2kdB43D+1hdbo8w0nUfsOp2j+Vv2To+N2M4YcdPavYdB8Q/2x9o/0XyfK2/8tN2c59h6V4fqulT6DcC1unjd3TzAYiSMEkdwPQ1QjuEGeGrzaNarhnZLTseRQxdXCy5WtD2jxl4r+w6eq/Yt+24Cf63GeG56e1eS6tqP9palNd+V5fmY+XdnGAB1x7VmvMuScHrULzrhhg9KVT2uIldoxxGJqYiWuxO8m3HFUM+Zx070E+Z07eteoeCPBGpaZrU0009oytbsgCOxOdynuo9K7KFBQVlq3+BphsM5s2/htom3w/pmp/aP+ev7vZ/tuvXP9K9AqCyga3tEicgsuc46dc1PXowjZWPo6UFCCigpkkaTRlHGVPUZp9FMs53WvB2jaqkk81h5t0sJSI+c6+pA4YDqe9cFqPw6vx5f2HSvXf8A6Qvtjq31r1+isp0IS3Rz1MLSqbo8Fk+HPiou23Svlycf6RF0/wC+qs2nw210vF9q0n5dw8z/AElOmeejele40VMcPGOzZksvop3/AMv8jz/RPhtpC+f/AGnpP93y/wDSX98/db6V3MFlb27l4o9rEYzknip6K2UUtjqhShBWigoooqiz/9k=</BINVAL>
<TYPE>image/jpeg</TYPE>
</PHOTO>
<EMAIL>
<WORK/>
<INTERNET/>
<PREF/>
<USERID>test@home.net</USERID>
</EMAIL>
<EMAIL>
<HOME/>
<INTERNET/>
<PREF/>
<USERID>test@home.net</USERID>
</EMAIL>
<TEL>
<HOME/>
<VOICE/>
<NUMBER>1234567</NUMBER>
</TEL>
</vCard>
</user>
</host>
</server-data>
<?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,
org.jivesoftware.util.ParamUtils,
org.jivesoftware.openfire.XMPPServer,
org.jivesoftware.openfire.plugin.ImportExportPlugin"
contentType="application/x-download"%><%
String fileName = request.getParameter("fileName");
boolean xep227Support = ParamUtils.getBooleanParameter(request, "xep227support", false);
response.setContentType("application/x-download");
response.setHeader("Content-Disposition","attachment;filename="+fileName+".xml");
ImportExportPlugin plugin = (ImportExportPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("userimportexport");
byte[] content = plugin.exportUsersToByteArray();
byte[] content = plugin.exportUsersToByteArray(xep227Support);
OutputStream os = response.getOutputStream();
os.write(content);
os.flush();
......
......@@ -12,6 +12,7 @@
boolean exportUsers = request.getParameter("exportUsers") != null;
boolean success = request.getParameter("success") != null;
boolean exportToFile = ParamUtils.getBooleanParameter(request, "exporttofile", true);
boolean xep227Support = ParamUtils.getBooleanParameter(request, "xep227support", false);
ImportExportPlugin plugin = (ImportExportPlugin)XMPPServer.getInstance().getPluginManager(
).getPlugin("userimportexport");
......@@ -26,12 +27,12 @@
errors.put("missingFile","missingFile");
}
else {
response.sendRedirect("export-file.jsp?fileName="+file);
response.sendRedirect("export-file.jsp?fileName="+file + ( xep227Support ? "&xep227support=true" : ""));
}
}
else {
try {
exportText = plugin.exportUsersToString();
exportText = plugin.exportUsersToString(xep227Support);
}
catch (IOException e) {
errors.put("IOException","IOException");
......@@ -94,7 +95,7 @@
<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="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>
......@@ -102,13 +103,17 @@
<td width="99%">Export File Name:&nbsp;<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="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%">&nbsp;</td>
<td width="99%"><textarea cols="80" rows="20" wrap=off><%=exportText %></textarea></td>
</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>
</table>
</div>
......
......@@ -10,6 +10,7 @@
<%
boolean importUsers = request.getParameter("importUsers") != null;
boolean xep227Support = request.getParameter("xep227support") != null;
ImportExportPlugin plugin = (ImportExportPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("userimportexport");
List<String> duplicateUsers = new ArrayList<String>();
......@@ -23,14 +24,16 @@
FileItem fi = (FileItem) i.next();
FileItem pd = (FileItem) i.next();
String previousDomain = pd.getString();
FileItem xsup = (FileItem) i.next();
xep227Support = new Boolean( xsup.getString() );
if (plugin.validateImportFile(fi)) {
if (plugin.validateImportFile(fi, xep227Support)) {
try {
if (isEmpty(previousDomain)) {
duplicateUsers.addAll(plugin.importUserData(fi, null));
duplicateUsers.addAll(plugin.importUserData(fi, null, xep227Support));
}
else if (!isEmpty(previousDomain)) {
duplicateUsers.addAll(plugin.importUserData(fi, previousDomain));
duplicateUsers.addAll(plugin.importUserData(fi, previousDomain, xep227Support));
}
else {
errors.put("missingDomain", "missingDomain");
......@@ -143,6 +146,10 @@ Use the form below to import a user data XML file.
See the migration section of the <a href="../../plugin-admin.jsp?plugin=userimportexport&showReadme=true&decorator=none">readme</a> for details.
</p>
Replace Domain: <input type="text" size="20" maxlength="150" name="previousDomain" value=""/>
<p>
<input type="checkbox" name="xep227support" <%= xep227Support ? "checked" : "" %> value="true" id="rb03"/> <label for="rb03"><b>XEP-0227</b></label> - Import using format defined in <a href="http://www.xmpp.org/extensions/xep-0227.html" target="_blank">XEP-0227</a>.
</div>
<input type="submit" value="Import">
......
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