mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 14:02:06 +01:00
improved Delay Information Parser (fixes SMACK-243)
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@11823 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
8b54f34153
commit
7e01c66374
8 changed files with 496 additions and 56 deletions
|
@ -23,19 +23,63 @@ package org.jivesoftware.smack.util;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of utility methods for String objects.
|
* A collection of utility methods for String objects.
|
||||||
*/
|
*/
|
||||||
public class StringUtils {
|
public class StringUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date format as defined in XEP-0082 - XMPP Date and Time Profiles. The time zone is set to
|
||||||
|
* UTC.
|
||||||
|
* <p>
|
||||||
|
* Date formats are not synchronized. Since multiple threads access the format concurrently, it
|
||||||
|
* must be synchronized externally or you can use the convenience methods
|
||||||
|
* {@link #parseXEP0082Date(String)} and {@link #formatXEP0082Date(Date)}.
|
||||||
|
*/
|
||||||
|
public static final DateFormat XEP_0082_UTC_FORMAT = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
||||||
|
static {
|
||||||
|
XEP_0082_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
}
|
||||||
|
|
||||||
private static final char[] QUOTE_ENCODE = """.toCharArray();
|
private static final char[] QUOTE_ENCODE = """.toCharArray();
|
||||||
private static final char[] APOS_ENCODE = "'".toCharArray();
|
private static final char[] APOS_ENCODE = "'".toCharArray();
|
||||||
private static final char[] AMP_ENCODE = "&".toCharArray();
|
private static final char[] AMP_ENCODE = "&".toCharArray();
|
||||||
private static final char[] LT_ENCODE = "<".toCharArray();
|
private static final char[] LT_ENCODE = "<".toCharArray();
|
||||||
private static final char[] GT_ENCODE = ">".toCharArray();
|
private static final char[] GT_ENCODE = ">".toCharArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given date string in the XEP-0082 - XMPP Date and Time Profiles format.
|
||||||
|
*
|
||||||
|
* @param dateString the date string to parse
|
||||||
|
* @return the parsed Date
|
||||||
|
* @throws ParseException if the specified string cannot be parsed
|
||||||
|
*/
|
||||||
|
public static Date parseXEP0082Date(String dateString) throws ParseException {
|
||||||
|
synchronized (XEP_0082_UTC_FORMAT) {
|
||||||
|
return XEP_0082_UTC_FORMAT.parse(dateString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a Date into a XEP-0082 - XMPP Date and Time Profiles string.
|
||||||
|
*
|
||||||
|
* @param date the time value to be formatted into a time string
|
||||||
|
* @return the formatted time string in XEP-0082 format
|
||||||
|
*/
|
||||||
|
public static String formatXEP0082Date(Date date) {
|
||||||
|
synchronized (XEP_0082_UTC_FORMAT) {
|
||||||
|
return XEP_0082_UTC_FORMAT.format(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name portion of a XMPP address. For example, for the
|
* Returns the name portion of a XMPP address. For example, for the
|
||||||
* address "matt@jivesoftware.com/Smack", "matt" would be returned. If no
|
* address "matt@jivesoftware.com/Smack", "matt" would be returned. If no
|
||||||
|
|
|
@ -15,12 +15,12 @@ package org.jivesoftware.smackx.packet;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.packet.DelayInformation;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A decorator for the {@link DelayInformation} class to transparently support
|
* A decorator for the {@link DelayInformation} class to transparently support
|
||||||
* both the new <b>Delay Delivery</b> specification <a href="http://xmpp.org/extensions/xep-0203.html">XEP-0203</a> and
|
* both the new <b>Delay Delivery</b> specification <a href="http://xmpp.org/extensions/xep-0203.html">XEP-0203</a> and
|
||||||
* the old one <a href="http://xmpp.org/extensions/xep-0203.html">XEP-0091</a>.
|
* the old one <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091</a>.
|
||||||
*
|
*
|
||||||
* Existing code can be backward compatible.
|
* Existing code can be backward compatible.
|
||||||
*
|
*
|
||||||
|
@ -28,8 +28,13 @@ import org.jivesoftware.smackx.packet.DelayInformation;
|
||||||
*/
|
*/
|
||||||
public class DelayInfo extends DelayInformation
|
public class DelayInfo extends DelayInformation
|
||||||
{
|
{
|
||||||
|
|
||||||
DelayInformation wrappedInfo;
|
DelayInformation wrappedInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance with given delay information.
|
||||||
|
* @param delay the delay information
|
||||||
|
*/
|
||||||
public DelayInfo(DelayInformation delay)
|
public DelayInfo(DelayInformation delay)
|
||||||
{
|
{
|
||||||
super(delay.getStamp());
|
super(delay.getStamp());
|
||||||
|
@ -84,9 +89,7 @@ public class DelayInfo extends DelayInformation
|
||||||
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
|
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
|
||||||
"\"");
|
"\"");
|
||||||
buf.append(" stamp=\"");
|
buf.append(" stamp=\"");
|
||||||
synchronized (NEW_UTC_FORMAT) {
|
buf.append(StringUtils.formatXEP0082Date(getStamp()));
|
||||||
buf.append(NEW_UTC_FORMAT.format(getStamp()));
|
|
||||||
}
|
|
||||||
buf.append("\"");
|
buf.append("\"");
|
||||||
if (getFrom() != null && getFrom().length() > 0) {
|
if (getFrom() != null && getFrom().length() > 0) {
|
||||||
buf.append(" from=\"").append(getFrom()).append("\"");
|
buf.append(" from=\"").append(getFrom()).append("\"");
|
||||||
|
|
|
@ -20,36 +20,38 @@
|
||||||
|
|
||||||
package org.jivesoftware.smackx.packet;
|
package org.jivesoftware.smackx.packet;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.PacketExtension;
|
import java.text.DateFormat;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents timestamp information about data stored for later delivery. A DelayInformation will
|
* Represents timestamp information about data stored for later delivery. A DelayInformation will
|
||||||
* always includes the timestamp when the packet was originally sent and may include more
|
* always includes the timestamp when the packet was originally sent and may include more
|
||||||
* information such as the JID of the entity that originally sent the packet as well as the reason
|
* information such as the JID of the entity that originally sent the packet as well as the reason
|
||||||
* for the dealy.<p>
|
* for the delay.<p>
|
||||||
*
|
*
|
||||||
* For more information see <a href="http://www.jabber.org/jeps/jep-0091.html">JEP-91</a>.
|
* For more information see <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091</a>
|
||||||
|
* and <a href="http://xmpp.org/extensions/xep-0203.html">XEP-0203</a>.
|
||||||
*
|
*
|
||||||
* @author Gaston Dombiak
|
* @author Gaston Dombiak
|
||||||
*/
|
*/
|
||||||
public class DelayInformation implements PacketExtension {
|
public class DelayInformation implements PacketExtension {
|
||||||
|
|
||||||
public static SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
|
|
||||||
/**
|
/**
|
||||||
* New date format based on JEP-82 that some clients may use when sending delayed dates.
|
* Date format according to the obsolete XEP-0091 specification.
|
||||||
* JEP-91 is using a SHOULD other servers or clients may be using this format instead of the
|
* XEP-0091 recommends to use this old format for date-time instead of
|
||||||
* old UTC format.
|
* the one specified in XEP-0082.
|
||||||
|
* <p>
|
||||||
|
* Date formats are not synchronized. Since multiple threads access the format concurrently,
|
||||||
|
* it must be synchronized externally.
|
||||||
*/
|
*/
|
||||||
public static SimpleDateFormat NEW_UTC_FORMAT =
|
public static final DateFormat XEP_0091_UTC_FORMAT = new SimpleDateFormat(
|
||||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
"yyyyMMdd'T'HH:mm:ss");
|
||||||
|
|
||||||
static {
|
static {
|
||||||
UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
XEP_0091_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
NEW_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Date stamp;
|
private Date stamp;
|
||||||
|
@ -58,6 +60,7 @@ public class DelayInformation implements PacketExtension {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance with the specified timestamp.
|
* Creates a new instance with the specified timestamp.
|
||||||
|
* @param stamp the timestamp
|
||||||
*/
|
*/
|
||||||
public DelayInformation(Date stamp) {
|
public DelayInformation(Date stamp) {
|
||||||
super();
|
super();
|
||||||
|
@ -86,10 +89,10 @@ public class DelayInformation implements PacketExtension {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the timstamp when the packet was originally sent. The returned Date is
|
* Returns the timestamp when the packet was originally sent. The returned Date is
|
||||||
* be understood as UTC.
|
* be understood as UTC.
|
||||||
*
|
*
|
||||||
* @return the timstamp when the packet was originally sent.
|
* @return the timestamp when the packet was originally sent.
|
||||||
*/
|
*/
|
||||||
public Date getStamp() {
|
public Date getStamp() {
|
||||||
return stamp;
|
return stamp;
|
||||||
|
@ -128,8 +131,8 @@ public class DelayInformation implements PacketExtension {
|
||||||
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
|
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
|
||||||
"\"");
|
"\"");
|
||||||
buf.append(" stamp=\"");
|
buf.append(" stamp=\"");
|
||||||
synchronized (UTC_FORMAT) {
|
synchronized (XEP_0091_UTC_FORMAT) {
|
||||||
buf.append(UTC_FORMAT.format(stamp));
|
buf.append(XEP_0091_UTC_FORMAT.format(stamp));
|
||||||
}
|
}
|
||||||
buf.append("\"");
|
buf.append("\"");
|
||||||
if (from != null && from.length() > 0) {
|
if (from != null && from.length() > 0) {
|
||||||
|
|
|
@ -19,12 +19,12 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.packet;
|
package org.jivesoftware.smackx.packet;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.PacketExtension;
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The process by which two entities initiate a stream.
|
* The process by which two entities initiate a stream.
|
||||||
*
|
*
|
||||||
|
@ -347,7 +347,7 @@ public class StreamInitiation extends IQ {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getDate() != null) {
|
if (getDate() != null) {
|
||||||
buffer.append("date=\"").append(DelayInformation.UTC_FORMAT.format(date)).append("\" ");
|
buffer.append("date=\"").append(StringUtils.formatXEP0082Date(date)).append("\" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getHash() != null) {
|
if (getHash() != null) {
|
||||||
|
|
|
@ -21,8 +21,9 @@ import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provider simply creates a {@link DelayInfo} decorator for the {@link DelayInformation} that
|
* This provider simply creates a {@link DelayInfo} decorator for the {@link DelayInformation} that
|
||||||
* is returned by the superclass. This allows the new code using <a href="http://xmpp.org/extensions/xep-0203">Delay Information XEP-0203</a> to be backward compatible
|
* is returned by the superclass. This allows the new code using
|
||||||
* with <a href="http://xmpp.org/extensions/xep-0091">XEP-0091</a>.
|
* <a href="http://xmpp.org/extensions/xep-0203.html">Delay Information XEP-0203</a> to be
|
||||||
|
* backward compatible with <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091</a>.
|
||||||
*
|
*
|
||||||
* <p>This provider must be registered in the <b>smack.properties</b> file for the element
|
* <p>This provider must be registered in the <b>smack.properties</b> file for the element
|
||||||
* <b>delay</b> with namespace <b>urn:xmpp:delay</b></p>
|
* <b>delay</b> with namespace <b>urn:xmpp:delay</b></p>
|
||||||
|
|
|
@ -20,56 +20,185 @@
|
||||||
|
|
||||||
package org.jivesoftware.smackx.provider;
|
package org.jivesoftware.smackx.provider;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.PacketExtension;
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.packet.DelayInformation;
|
import org.jivesoftware.smackx.packet.DelayInformation;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The DelayInformationProvider parses DelayInformation packets.
|
* The DelayInformationProvider parses DelayInformation packets.
|
||||||
*
|
*
|
||||||
* @author Gaston Dombiak
|
* @author Gaston Dombiak
|
||||||
|
* @author Henning Staib
|
||||||
*/
|
*/
|
||||||
public class DelayInformationProvider implements PacketExtensionProvider {
|
public class DelayInformationProvider implements PacketExtensionProvider {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Date format used to parse dates in the XEP-0091 format but missing leading
|
||||||
|
* zeros for month and day.
|
||||||
|
*/
|
||||||
|
private static final SimpleDateFormat XEP_0091_UTC_FALLBACK_FORMAT = new SimpleDateFormat(
|
||||||
|
"yyyyMd'T'HH:mm:ss");
|
||||||
|
static {
|
||||||
|
XEP_0091_UTC_FALLBACK_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Date format used to parse dates in the XEP-0082 format but missing milliseconds.
|
||||||
|
*/
|
||||||
|
private static final SimpleDateFormat XEP_0082_UTC_FORMAT_WITHOUT_MILLIS = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||||
|
static {
|
||||||
|
XEP_0082_UTC_FORMAT_WITHOUT_MILLIS.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maps a regular expression for a date format to the date format parser.
|
||||||
|
*/
|
||||||
|
private static Map<String, DateFormat> formats = new HashMap<String, DateFormat>();
|
||||||
|
static {
|
||||||
|
formats.put("^\\d+T\\d+:\\d+:\\d+$", DelayInformation.XEP_0091_UTC_FORMAT);
|
||||||
|
formats.put("^\\d+-\\d+-\\d+T\\d+:\\d+:\\d+\\.\\d+Z$", StringUtils.XEP_0082_UTC_FORMAT);
|
||||||
|
formats.put("^\\d+-\\d+-\\d+T\\d+:\\d+:\\d+Z$", XEP_0082_UTC_FORMAT_WITHOUT_MILLIS);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new DeliveryInformationProvider.
|
* Creates a new DeliveryInformationProvider. ProviderManager requires that
|
||||||
* ProviderManager requires that every PacketExtensionProvider has a public, no-argument
|
* every PacketExtensionProvider has a public, no-argument constructor
|
||||||
* constructor
|
|
||||||
*/
|
*/
|
||||||
public DelayInformationProvider() {
|
public DelayInformationProvider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
|
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
|
||||||
|
String stampString = (parser.getAttributeValue("", "stamp"));
|
||||||
Date stamp = null;
|
Date stamp = null;
|
||||||
try {
|
DateFormat format = null;
|
||||||
synchronized (DelayInformation.UTC_FORMAT) {
|
|
||||||
stamp = DelayInformation.UTC_FORMAT.parse(parser.getAttributeValue("", "stamp"));
|
for (String regexp : formats.keySet()) {
|
||||||
}
|
if (stampString.matches(regexp)) {
|
||||||
} catch (ParseException e) {
|
try {
|
||||||
// Try again but assuming that the date follows JEP-82 format
|
format = formats.get(regexp);
|
||||||
// (Jabber Date and Time Profiles)
|
synchronized (format) {
|
||||||
try {
|
stamp = format.parse(stampString);
|
||||||
synchronized (DelayInformation.NEW_UTC_FORMAT) {
|
}
|
||||||
stamp = DelayInformation.NEW_UTC_FORMAT
|
|
||||||
.parse(parser.getAttributeValue("", "stamp"));
|
|
||||||
}
|
}
|
||||||
} catch (ParseException e1) {
|
catch (ParseException e) {
|
||||||
// Last attempt. Try parsing the date assuming that it does not include milliseconds
|
// do nothing, format is still set
|
||||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
}
|
||||||
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
||||||
stamp = formatter.parse(parser.getAttributeValue("", "stamp"));
|
// break because only one regexp can match
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if date is in XEP-0091 format handle ambiguous dates missing the
|
||||||
|
* leading zero in month and day
|
||||||
|
*/
|
||||||
|
if (format == DelayInformation.XEP_0091_UTC_FORMAT
|
||||||
|
&& stampString.split("T")[0].length() < 8) {
|
||||||
|
stamp = handleDateWithMissingLeadingZeros(stampString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if date could not be parsed but XML is valid, don't shutdown
|
||||||
|
* connection by throwing an exception instead set timestamp to current
|
||||||
|
* time
|
||||||
|
*/
|
||||||
|
if (stamp == null) {
|
||||||
|
stamp = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
DelayInformation delayInformation = new DelayInformation(stamp);
|
DelayInformation delayInformation = new DelayInformation(stamp);
|
||||||
delayInformation.setFrom(parser.getAttributeValue("", "from"));
|
delayInformation.setFrom(parser.getAttributeValue("", "from"));
|
||||||
delayInformation.setReason(parser.nextText());
|
String reason = parser.nextText();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parser.nextText() returns empty string if there is no reason.
|
||||||
|
* DelayInformation API specifies that null should be returned in that
|
||||||
|
* case.
|
||||||
|
*/
|
||||||
|
reason = "".equals(reason) ? null : reason;
|
||||||
|
delayInformation.setReason(reason);
|
||||||
|
|
||||||
return delayInformation;
|
return delayInformation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given date string in different ways and returns the date that
|
||||||
|
* lies in the past and/or is nearest to the current date-time.
|
||||||
|
*
|
||||||
|
* @param stampString date in string representation
|
||||||
|
* @return the parsed date
|
||||||
|
*/
|
||||||
|
private Date handleDateWithMissingLeadingZeros(String stampString) {
|
||||||
|
Calendar now = new GregorianCalendar();
|
||||||
|
Calendar xep91 = null;
|
||||||
|
Calendar xep91Fallback = null;
|
||||||
|
|
||||||
|
xep91 = parseXEP91Date(stampString, DelayInformation.XEP_0091_UTC_FORMAT);
|
||||||
|
xep91Fallback = parseXEP91Date(stampString, XEP_0091_UTC_FALLBACK_FORMAT);
|
||||||
|
|
||||||
|
List<Calendar> dates = filterDatesBefore(now, xep91, xep91Fallback);
|
||||||
|
|
||||||
|
if (!dates.isEmpty()) {
|
||||||
|
return determineNearestDate(now, dates).getTime();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Calendar parseXEP91Date(String stampString, DateFormat dateFormat) {
|
||||||
|
try {
|
||||||
|
synchronized (dateFormat) {
|
||||||
|
dateFormat.parse(stampString);
|
||||||
|
return dateFormat.getCalendar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ParseException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Calendar> filterDatesBefore(Calendar now, Calendar... dates) {
|
||||||
|
List<Calendar> result = new ArrayList<Calendar>();
|
||||||
|
|
||||||
|
for (Calendar calendar : dates) {
|
||||||
|
if (calendar != null && calendar.before(now)) {
|
||||||
|
result.add(calendar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Calendar determineNearestDate(final Calendar now, List<Calendar> dates) {
|
||||||
|
|
||||||
|
Collections.sort(dates, new Comparator<Calendar>() {
|
||||||
|
|
||||||
|
public int compare(Calendar o1, Calendar o2) {
|
||||||
|
Long diff1 = new Long(now.getTimeInMillis() - o1.getTimeInMillis());
|
||||||
|
Long diff2 = new Long(now.getTimeInMillis() - o2.getTimeInMillis());
|
||||||
|
return diff1.compareTo(diff2);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return dates.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,13 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.provider;
|
package org.jivesoftware.smackx.provider;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.provider.IQProvider;
|
import org.jivesoftware.smack.provider.IQProvider;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.packet.DataForm;
|
import org.jivesoftware.smackx.packet.DataForm;
|
||||||
import org.jivesoftware.smackx.packet.DelayInformation;
|
|
||||||
import org.jivesoftware.smackx.packet.StreamInitiation;
|
import org.jivesoftware.smackx.packet.StreamInitiation;
|
||||||
import org.jivesoftware.smackx.packet.StreamInitiation.File;
|
import org.jivesoftware.smackx.packet.StreamInitiation.File;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
@ -90,10 +93,19 @@ public class StreamInitiationProvider implements IQProvider {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Date fileDate = new Date();
|
||||||
|
if (date != null) {
|
||||||
|
try {
|
||||||
|
fileDate = StringUtils.parseXEP0082Date(date);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
// couldn't parse date, use current date-time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File file = new File(name, fileSize);
|
File file = new File(name, fileSize);
|
||||||
file.setHash(hash);
|
file.setHash(hash);
|
||||||
if (date != null)
|
file.setDate(fileDate);
|
||||||
file.setDate(DelayInformation.UTC_FORMAT.parse(date));
|
|
||||||
file.setDesc(desc);
|
file.setDesc(desc);
|
||||||
file.setRanged(isRanged);
|
file.setRanged(isRanged);
|
||||||
initiation.setFile(file);
|
initiation.setFile(file);
|
||||||
|
|
|
@ -0,0 +1,248 @@
|
||||||
|
/**
|
||||||
|
* All rights reserved. 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.smackx.provider;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
import org.jivesoftware.smackx.packet.DelayInfo;
|
||||||
|
import org.jivesoftware.smackx.packet.DelayInformation;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.xmlpull.mxp1.MXParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import com.jamesmurty.utils.XMLBuilder;
|
||||||
|
|
||||||
|
public class DelayInformationTest {
|
||||||
|
|
||||||
|
private static Properties outputProperties = new Properties();
|
||||||
|
static {
|
||||||
|
outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void delayInformationTest() throws Exception {
|
||||||
|
DelayInformationProvider p = new DelayInformationProvider();
|
||||||
|
DelayInformation delayInfo;
|
||||||
|
XmlPullParser parser;
|
||||||
|
String control;
|
||||||
|
GregorianCalendar calendar = new GregorianCalendar(2002, 9 - 1, 10, 23, 8, 25);
|
||||||
|
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
Date date = calendar.getTime();
|
||||||
|
|
||||||
|
control = XMLBuilder.create("x")
|
||||||
|
.a("xmlns", "jabber:x:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "2002-09-10T23:08:25Z")
|
||||||
|
.t("Offline Storage")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
parser = getParser(control, "x");
|
||||||
|
delayInfo = (DelayInformation) p.parseExtension(parser);
|
||||||
|
|
||||||
|
assertEquals("capulet.com", delayInfo.getFrom());
|
||||||
|
assertEquals(date, delayInfo.getStamp());
|
||||||
|
assertEquals("Offline Storage", delayInfo.getReason());
|
||||||
|
|
||||||
|
assertEquals(XmlPullParser.END_TAG, parser.getEventType());
|
||||||
|
assertEquals("x", parser.getName());
|
||||||
|
|
||||||
|
control = XMLBuilder.create("x")
|
||||||
|
.a("xmlns", "jabber:x:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "2002-09-10T23:08:25Z")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
parser = getParser(control, "x");
|
||||||
|
delayInfo = (DelayInformation) p.parseExtension(parser);
|
||||||
|
|
||||||
|
assertEquals("capulet.com", delayInfo.getFrom());
|
||||||
|
assertEquals(date, delayInfo.getStamp());
|
||||||
|
assertNull(delayInfo.getReason());
|
||||||
|
|
||||||
|
assertEquals(XmlPullParser.END_TAG, parser.getEventType());
|
||||||
|
assertEquals("x", parser.getName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void delayInfoTest() throws Exception {
|
||||||
|
DelayInformationProvider p = new DelayInfoProvider();
|
||||||
|
DelayInfo delayInfo;
|
||||||
|
XmlPullParser parser;
|
||||||
|
String control;
|
||||||
|
GregorianCalendar calendar = new GregorianCalendar(2002, 9 - 1, 10, 23, 8, 25);
|
||||||
|
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
Date date = calendar.getTime();
|
||||||
|
|
||||||
|
control = XMLBuilder.create("delay")
|
||||||
|
.a("xmlns", "urn:xmpp:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "2002-09-10T23:08:25Z")
|
||||||
|
.t("Offline Storage")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
parser = getParser(control, "delay");
|
||||||
|
delayInfo = (DelayInfo) p.parseExtension(parser);
|
||||||
|
|
||||||
|
assertEquals("capulet.com", delayInfo.getFrom());
|
||||||
|
assertEquals(date, delayInfo.getStamp());
|
||||||
|
assertEquals("Offline Storage", delayInfo.getReason());
|
||||||
|
|
||||||
|
assertEquals(XmlPullParser.END_TAG, parser.getEventType());
|
||||||
|
assertEquals("delay", parser.getName());
|
||||||
|
|
||||||
|
control = XMLBuilder.create("delay")
|
||||||
|
.a("xmlns", "urn:xmpp:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "2002-09-10T23:08:25Z")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
parser = getParser(control, "delay");
|
||||||
|
delayInfo = (DelayInfo) p.parseExtension(parser);
|
||||||
|
|
||||||
|
assertEquals("capulet.com", delayInfo.getFrom());
|
||||||
|
assertEquals(date, delayInfo.getStamp());
|
||||||
|
assertNull(delayInfo.getReason());
|
||||||
|
|
||||||
|
assertEquals(XmlPullParser.END_TAG, parser.getEventType());
|
||||||
|
assertEquals("delay", parser.getName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void dateFormatsTest() throws Exception {
|
||||||
|
DelayInformationProvider p = new DelayInfoProvider();
|
||||||
|
DelayInfo delayInfo;
|
||||||
|
String control;
|
||||||
|
GregorianCalendar calendar = new GregorianCalendar(2002, 9 - 1, 10, 23, 8, 25);
|
||||||
|
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
|
||||||
|
// XEP-0082 date format
|
||||||
|
control = XMLBuilder.create("delay")
|
||||||
|
.a("xmlns", "urn:xmpp:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "2002-09-10T23:08:25.12Z")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
|
||||||
|
|
||||||
|
GregorianCalendar cal = (GregorianCalendar) calendar.clone();
|
||||||
|
cal.add(Calendar.MILLISECOND, 12);
|
||||||
|
assertEquals(cal.getTime(), delayInfo.getStamp());
|
||||||
|
|
||||||
|
// XEP-0082 date format without milliseconds
|
||||||
|
control = XMLBuilder.create("delay")
|
||||||
|
.a("xmlns", "urn:xmpp:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "2002-09-10T23:08:25Z")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
|
||||||
|
|
||||||
|
assertEquals(calendar.getTime(), delayInfo.getStamp());
|
||||||
|
|
||||||
|
// XEP-0082 date format without milliseconds and leading 0 in month
|
||||||
|
control = XMLBuilder.create("delay")
|
||||||
|
.a("xmlns", "urn:xmpp:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "2002-9-10T23:08:25Z")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
|
||||||
|
|
||||||
|
assertEquals(calendar.getTime(), delayInfo.getStamp());
|
||||||
|
|
||||||
|
// XEP-0091 date format
|
||||||
|
control = XMLBuilder.create("delay")
|
||||||
|
.a("xmlns", "urn:xmpp:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "20020910T23:08:25")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
|
||||||
|
|
||||||
|
assertEquals(calendar.getTime(), delayInfo.getStamp());
|
||||||
|
|
||||||
|
// XEP-0091 date format without leading 0 in month
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMd'T'HH:mm:ss");
|
||||||
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
GregorianCalendar dateInPast = new GregorianCalendar();
|
||||||
|
if (dateInPast.get(Calendar.MONTH) >= 10) {
|
||||||
|
dateInPast.set(Calendar.MONTH, dateInPast.get(Calendar.MONTH) - 3);
|
||||||
|
}
|
||||||
|
dateInPast.add(Calendar.DAY_OF_MONTH, -3);
|
||||||
|
dateInPast.set(Calendar.MILLISECOND, 0);
|
||||||
|
|
||||||
|
control = XMLBuilder.create("delay")
|
||||||
|
.a("xmlns", "urn:xmpp:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", dateFormat.format(dateInPast.getTime()))
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
|
||||||
|
|
||||||
|
assertEquals(dateInPast.getTime(), delayInfo.getStamp());
|
||||||
|
|
||||||
|
// XEP-0091 date format from SMACK-243
|
||||||
|
control = XMLBuilder.create("delay")
|
||||||
|
.a("xmlns", "urn:xmpp:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "200868T09:16:20")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
|
||||||
|
Date controlDate = StringUtils.parseXEP0082Date("2008-06-08T09:16:20.0Z");
|
||||||
|
|
||||||
|
assertEquals(controlDate, delayInfo.getStamp());
|
||||||
|
|
||||||
|
// invalid date format
|
||||||
|
control = XMLBuilder.create("delay")
|
||||||
|
.a("xmlns", "urn:xmpp:delay")
|
||||||
|
.a("from", "capulet.com")
|
||||||
|
.a("stamp", "yesterday")
|
||||||
|
.asString(outputProperties);
|
||||||
|
|
||||||
|
delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
|
||||||
|
|
||||||
|
assertNotNull(delayInfo.getStamp());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private XmlPullParser getParser(String control, String startTag)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
XmlPullParser parser = new MXParser();
|
||||||
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||||
|
parser.setInput(new StringReader(control));
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (parser.next() == XmlPullParser.START_TAG
|
||||||
|
&& parser.getName().equals(startTag)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue