diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md index 794a978705..771afac33d 100644 --- a/documentation/extensions/index.md +++ b/documentation/extensions/index.md @@ -87,6 +87,7 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental | [Internet of Things - Discovery](iot.md) | [XEP-0347](http://xmpp.org/extensions/xep-0347.html) | Describes how Things can be installed and discovered by their owners. | | Google GCM JSON payload | n/a | Semantically the same as XEP-0335: JSON Containers | | Client State Indication | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. | +| [Message Archive Management](mam.md) | [XEP-0313](http://xmpp.org/extensions/xep-0313.html) | Query and control an archive of messages stored on a server. | Legacy Smack Extensions and currently supported XEPs of smack-legacy diff --git a/documentation/extensions/mam.md b/documentation/extensions/mam.md new file mode 100644 index 0000000000..ed0de6b03c --- /dev/null +++ b/documentation/extensions/mam.md @@ -0,0 +1,192 @@ +Message Archive Management +========================== + +Query and control an archive of messages stored on a server. + + * Check MAM support + * Query archive + * Paging + * Get form fields + * Get preferences + * Update preferences + + +**XEP related:** [XEP-0313](http://xmpp.org/extensions/xep-0313.html) + + +Get an instance of Message Archive Management Manager +----------------------------------------------------- + +``` +MamManager mamManager = MamManager.getInstanceFor(connection); +``` + + +Check MAM support +----------------- + +``` +boolean isSupported = mamManager.isSupportedByServer(); +``` + + +Query archive +------------- + +``` +MamQueryResult mamQueryResult = mamManager.queryArchive(max); +``` +*max* is an `Integer` + +or + +``` +MamQueryResult mamQueryResult = mamManager.queryArchive(withJid); +``` +*withJid* is a `Jid` + +or + +``` +MamQueryResult mamQueryResult = mamManager.queryArchive(start, end); +``` +*start* is a `Date` + +*end* is a `Date` + +or + +``` +MamQueryResult mamQueryResult = mamManager.queryArchive(additionalFields); +``` +*additionalFields* is a `List` + +or + +``` +MamQueryResult mamQueryResult = mamManager.queryArchiveWithStartDate(start); +``` +*start* is a `Date` + +or + +``` +MamQueryResult mamQueryResult = mamManager.queryArchiveWithEndDate(end); +``` +*end* is a `Date` + +or + +``` +MamQueryResult mamQueryResult = mamManager.queryArchive(max, start, end, withJid, additionalFields); +``` +*max* is an `Integer` + +*start* is a `Date` + +*end* is a `Date` + +*withJid* is a `Jid` + +*additionalFields* is a `List` + + +**Get data from mamQueryResult object** + +``` +// Get forwarded messages +List forwardedMessages = mamQueryResult.forwardedMessages; + +// Get fin IQ +MamFinIQ mamFinIQ = mamQueryResult.mamFinIQ; +``` + + +Paging +------ + +**Get a page** + +``` +MamQueryResult mamQueryResult = mamManager.page(dataForm, rsmSet); +``` +*dataForm* is a `DataForm` + +*rsmSet* is a `RSMSet` + + +**Get the next page** + +``` +MamQueryResult mamQueryResult = mamManager.pageNext(previousMamQueryResult, count); +``` +*previousMamQueryResult* is a `MamQueryResult` + +*count* is an `int` + + +**Get page before the first message saved (specific chat)** + +``` +MamQueryResult mamQueryResult = mamManager.pageBefore(chatJid, firstMessageId, max); +``` +*chatJid* is a `Jid` + +*firstMessageId* is a `String` + +*max* is an `int` + + +**Get page after the last message saved (specific chat)** + +``` +MamQueryResult mamQueryResult = mamManager.pageAfter(chatJid, lastMessageId, max); +``` +*chatJid* is a `Jid` + +*lastMessageId* is a `String` + +*max* is an `int` + + +Get form fields +--------------- + +``` +List formFields = mamManager.retrieveFormFields(); +``` + + +Get preferences +--------------- + +``` +MamPrefsResult mamPrefsResult = mamManager.retrieveArchivingPreferences(); + +// Get preferences IQ +MamPrefsIQ mamPrefs = mamPrefsResult.mamPrefs; + +// Obtain always and never list +List alwaysJids = mamPrefs.getAlwaysJids(); +List neverJids = mamPrefs.getNeverJids(); + +// Obtain default behaviour (can be 'always', 'never' or 'roster') +DefaultBehavior defaultBehavior = mamPrefs.getDefault(); + +// Get the data form +DataForm dataForm = mamPrefs.form; +``` + + +Update preferences +------------------ + +``` +MamPrefsResult mamPrefsResult = mamManager.updateArchivingPreferences(alwaysJids, neverJids, defaultBehavior); +``` +*alwaysJids* is a `List` + +*neverJids* is a `List` + +*defaultBehavior* is a `DefaultBehavior` + diff --git a/resources/releasedocs/README.html b/resources/releasedocs/README.html index 962c43fbc8..195858a8f1 100644 --- a/resources/releasedocs/README.html +++ b/resources/releasedocs/README.html @@ -155,13 +155,28 @@

Smack Readme

Thank you for downloading Smack! This version of Smack is compatible -with JVMs @targetCompatibility@ or higher. If you dont' use a +with JVMs @targetCompatibility@ or higher. Using a build system which +is able to consume Maven artifacts, like gradle or Maven, is highly +recommended when using Smack. +

+ +

+ This is not the real README. Please visit +

+ https://www.igniterealtime.org/projects/smack/readme +
+ for the README of the current stable Smack version. +

+ +

+If you dont' use a dependency resolution system, like gradle or maven, then you will need to download at least the Xml Pull Parser 3rd Edition (XPP3) library or any other library that implements the XmlPullParser interface -(like kXML). +(like kXML) and the set of +jXMPP libraries.

@@ -174,21 +189,14 @@

Smack Readme

online forum.

-

About the Distribution

- -The smack-core.jar file in the main distribution folder. The optional -smack-extensions.jar contains the Smack extensions -while smack-debug.jar contains an enhanced debugger.

-

Changelog and Upgrading

View the changelog for a list of changes since the last release. -If you are upgrading from Smack 3 to Smack 4, then please consult the Smack 4 Readme and Upgrade Guide

License Agreements

    -
  • Use of the Smack source code is governed by the Apache License: +
  • Use of the Smack source code is governed by the Apache License Version 2.0:
      Copyright 2002-2008 Jive Software.
     
    diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html
    index b844e1e00b..01c8a003ba 100644
    --- a/resources/releasedocs/changelog.html
    +++ b/resources/releasedocs/changelog.html
    @@ -141,6 +141,21 @@ 

    Smack Changelog

    +

    4.1.8 -- 2016-07-30

    + +

    Bug +

    +
      +
    • [SMACK-722] - SASL X-OAUTH2 implementation incorrectly performs Base64 encoding twice +
    • +
    • [SMACK-724] - Do not re-use the Socket after connect() failed. +
    • +
    • [SMACK-725] - ReconnectionManager should handle AlreadyConnectedException and AlreadyLoggedInException not as failure +
    • +
    • [SMACK-726] - 'purge' and 'remove' IQ of XEP-0013 must be of type 'set' +
    • +
    +

    4.1.7 -- 2016-04-14

    Bug diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java index 17d1d3f23f..2a6321e1ef 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java @@ -27,6 +27,8 @@ import org.jivesoftware.smack.util.TypedCloneable; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; /** * Represents XMPP message packets. A message can be one of several types: @@ -101,6 +103,17 @@ public Message(Jid to, String body) { setBody(body); } + /** + * Creates a new message to the specified recipient and with the specified body. + * + * @param to the user to send the message to. + * @param body the body of the message. + * @throws XmppStringprepException if 'to' is not a valid XMPP address. + */ + public Message(String to, String body) throws XmppStringprepException { + this(JidCreate.from(to), body); + } + /** * Copy constructor. *

    diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java index c483b4bf9a..aed2905b18 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java @@ -148,6 +148,19 @@ public boolean hasStanzaIdSet() { return id != null; } + /** + * Set the stanza id if none is set. + * + * @return the stanza id. + * @since 4.2 + */ + public String setStanzaId() { + if (!hasStanzaIdSet()) { + setStanzaId(StanzaIdUtil.newStanzaId()); + } + return getStanzaId(); + } + /** * Returns who the stanza(/packet) is being sent "to", or null if * the value is not set. The XMPP protocol often makes the "to" diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/IoTDiscoveryManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/IoTDiscoveryManager.java index 8851f84e1e..f1ce9c3625 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/IoTDiscoveryManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/IoTDiscoveryManager.java @@ -36,6 +36,7 @@ import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.iot.Thing; @@ -202,7 +203,7 @@ public IQ handleIQRequest(IQ iqRequest) { /** * Try to find an XMPP IoT registry. * - * @return the JID of a Thing Registry if one could be found. + * @return the JID of a Thing Registry if one could be found, null otherwise. * @throws InterruptedException * @throws NotConnectedException * @throws XMPPErrorException @@ -373,9 +374,11 @@ public void disownThing(Jid registry, Jid thing, NodeInfo nodeInfo) // Registry utility methods public boolean isRegistry(BareJid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + Objects.requireNonNull(jid, "JID argument must not be null"); // At some point 'usedRegistries' will also contain the registry returned by findRegistry(), but since this is // not the case from the beginning, we perform findRegistry().equals(jid) too. - if (findRegistry().equals(jid)) { + Jid registry = findRegistry(); + if (jid.equals(registry)) { return true; } if (usedRegistries.contains(jid)) { diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/IoTProvisioningManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/IoTProvisioningManager.java index 077a068827..d9264dfca1 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/IoTProvisioningManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/IoTProvisioningManager.java @@ -151,7 +151,7 @@ public IQ handleIQRequest(IQ iqRequest) { }); roster = Roster.getInstanceFor(connection); - roster.setSubscribeListener(new SubscribeListener() { + roster.addSubscribeListener(new SubscribeListener() { @Override public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { // First check if the subscription request comes from a known registry and accept the request if so. diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java new file mode 100644 index 0000000000..2f70553ed8 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java @@ -0,0 +1,517 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.SmackException.NoResponseException; +import org.jivesoftware.smack.SmackException.NotConnectedException; +import org.jivesoftware.smack.SmackException.NotLoggedInException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPConnectionRegistry; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.filter.IQReplyFilter; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamFinIQ; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.mam.filter.MamResultFilter; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.jxmpp.jid.Jid; +import org.jxmpp.util.XmppDateTime; + +/** + * A Manager for Message Archive Management (XEP-0313). + * + * @see XEP-0313: Message + * Archive Management + * @author Florian Schmaus + * @author Fernando Ramirez + * + */ +public final class MamManager extends Manager { + + static { + XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(XMPPConnection connection) { + getInstanceFor(connection); + } + }); + } + + private static final Map INSTANCES = new WeakHashMap<>(); + + /** + * Get the singleton instance of MamManager. + * + * @param connection + * @return the instance of MamManager + */ + public static synchronized MamManager getInstanceFor(XMPPConnection connection) { + MamManager mamManager = INSTANCES.get(connection); + + if (mamManager == null) { + mamManager = new MamManager(connection); + INSTANCES.put(connection, mamManager); + } + + return mamManager; + } + + private MamManager(XMPPConnection connection) { + super(connection); + } + + /** + * Query archive with a maximum amount of results. + * + * @param max + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(Integer max) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(max, null, null, null, null); + } + + /** + * Query archive with a JID (only messages from/to the JID). + * + * @param withJid + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(Jid withJid) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, null, null, withJid, null); + } + + /** + * Query archive filtering by start and/or end date. If start == null, the + * value of 'start' will be equal to the date/time of the earliest message + * stored in the archive. If end == null, the value of 'end' will be equal + * to the date/time of the most recent message stored in the archive. + * + * @param start + * @param end + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(Date start, Date end) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, start, end, null, null); + } + + /** + * Query Archive adding filters with additional fields. + * + * @param additionalFields + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(List additionalFields) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, null, null, null, additionalFields); + } + + /** + * Query archive filtering by start date. The value of 'end' will be equal + * to the date/time of the most recent message stored in the archive. + * + * @param start + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchiveWithStartDate(Date start) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, start, null, null, null); + } + + /** + * Query archive filtering by end date. The value of 'start' will be equal + * to the date/time of the earliest message stored in the archive. + * + * @param end + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchiveWithEndDate(Date end) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, null, end, null, null); + } + + /** + * Query archive applying filters: max count, start date, end date, from/to + * JID and with additional fields. + * + * @param max + * @param start + * @param end + * @param withJid + * @param additionalFields + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(Integer max, Date start, Date end, Jid withJid, List additionalFields) + throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, + NotLoggedInException { + DataForm dataForm = null; + String queryId = UUID.randomUUID().toString(); + + if (start != null || end != null || withJid != null || additionalFields != null) { + dataForm = getNewMamForm(); + addStart(start, dataForm); + addEnd(end, dataForm); + addWithJid(withJid, dataForm); + addAdditionalFields(additionalFields, dataForm); + } + + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + mamQueryIQ.setType(IQ.Type.set); + + addResultsLimit(max, mamQueryIQ); + return queryArchive(mamQueryIQ); + } + + private static void addAdditionalFields(List additionalFields, DataForm dataForm) { + if (additionalFields == null) { + return; + } + for (FormField formField : additionalFields) { + dataForm.addField(formField); + } + } + + private static void addResultsLimit(Integer max, MamQueryIQ mamQueryIQ) { + if (max == null) { + return; + } + RSMSet rsmSet = new RSMSet(max); + mamQueryIQ.addExtension(rsmSet); + + } + + private static void addWithJid(Jid withJid, DataForm dataForm) { + if (withJid == null) { + return; + } + FormField formField = new FormField("with"); + formField.addValue(withJid.toString()); + dataForm.addField(formField); + } + + private static void addEnd(Date end, DataForm dataForm) { + if (end == null) { + return; + } + FormField formField = new FormField("end"); + formField.addValue(XmppDateTime.formatXEP0082Date(end)); + dataForm.addField(formField); + } + + private static void addStart(Date start, DataForm dataForm) { + if (start == null) { + return; + } + FormField formField = new FormField("start"); + formField.addValue(XmppDateTime.formatXEP0082Date(start)); + dataForm.addField(formField); + } + + /** + * Returns a page of the archive. + * + * @param dataForm + * @param rsmSet + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult page(DataForm dataForm, RSMSet rsmSet) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + MamQueryIQ mamQueryIQ = new MamQueryIQ(UUID.randomUUID().toString(), dataForm); + mamQueryIQ.setType(IQ.Type.set); + mamQueryIQ.addExtension(rsmSet); + return queryArchive(mamQueryIQ); + } + + /** + * Returns the next page of the archive. + * + * @param mamQueryResult + * is the previous query result + * @param count + * is the amount of messages that a page contains + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult pageNext(MamQueryResult mamQueryResult, int count) throws NoResponseException, + XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { + RSMSet previousResultRsmSet = mamQueryResult.mamFin.getRSMSet(); + RSMSet requestRsmSet = new RSMSet(count, previousResultRsmSet.getLast(), RSMSet.PageDirection.after); + return page(mamQueryResult.form, requestRsmSet); + } + + /** + * Obtain page before the first message saved (specific chat). + * + * @param chatJid + * @param firstMessageId + * @param max + * @return the MAM query result + * @throws XMPPErrorException + * @throws NotLoggedInException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NoResponseException + */ + public MamQueryResult pageBefore(Jid chatJid, String firstMessageId, int max) throws XMPPErrorException, + NotLoggedInException, NotConnectedException, InterruptedException, NoResponseException { + RSMSet rsmSet = new RSMSet(null, firstMessageId, -1, -1, null, max, null, -1); + DataForm dataForm = getNewMamForm(); + addWithJid(chatJid, dataForm); + return page(dataForm, rsmSet); + } + + /** + * Obtain page after the last message saved (specific chat). + * + * @param chatJid + * @param lastMessageId + * @param max + * @return the MAM query result + * @throws XMPPErrorException + * @throws NotLoggedInException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NoResponseException + */ + public MamQueryResult pageAfter(Jid chatJid, String lastMessageId, int max) throws XMPPErrorException, + NotLoggedInException, NotConnectedException, InterruptedException, NoResponseException { + RSMSet rsmSet = new RSMSet(lastMessageId, null, -1, -1, null, max, null, -1); + DataForm dataForm = getNewMamForm(); + addWithJid(chatJid, dataForm); + return page(dataForm, rsmSet); + } + + /** + * Get the form fields supported by the server. + * + * @return the list of form fields. + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public List retrieveFormFields() throws NoResponseException, XMPPErrorException, NotConnectedException, + InterruptedException, NotLoggedInException { + String queryId = UUID.randomUUID().toString(); + MamQueryIQ mamQueryIq = new MamQueryIQ(queryId); + + MamQueryIQ mamResponseQueryIq = connection().createPacketCollectorAndSend(mamQueryIq).nextResultOrThrow(); + + return mamResponseQueryIq.getDataForm().getFields(); + } + + private MamQueryResult queryArchive(MamQueryIQ mamQueryIq) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + final XMPPConnection connection = getAuthenticatedConnectionOrThrow(); + MamFinIQ mamFinIQ = null; + + PacketCollector mamFinIQCollector = connection.createPacketCollector(new IQReplyFilter(mamQueryIq, connection)); + + PacketCollector.Configuration resultCollectorConfiguration = PacketCollector.newConfiguration() + .setStanzaFilter(new MamResultFilter(mamQueryIq)).setCollectorToReset(mamFinIQCollector); + PacketCollector resultCollector = connection.createPacketCollector(resultCollectorConfiguration); + + try { + connection.sendStanza(mamQueryIq); + mamFinIQ = mamFinIQCollector.nextResultOrThrow(); + } finally { + mamFinIQCollector.cancel(); + resultCollector.cancel(); + } + + List forwardedMessages = new ArrayList<>(resultCollector.getCollectedCount()); + + for (Message resultMessage = resultCollector + .pollResult(); resultMessage != null; resultMessage = resultCollector.pollResult()) { + MamElements.MamResultExtension mamResultExtension = MamElements.MamResultExtension.from(resultMessage); + forwardedMessages.add(mamResultExtension.getForwarded()); + } + + return new MamQueryResult(forwardedMessages, mamFinIQ, DataForm.from(mamQueryIq)); + } + + /** + * MAM query result class. + * + */ + public final static class MamQueryResult { + public final List forwardedMessages; + public final MamFinIQ mamFin; + private final DataForm form; + + private MamQueryResult(List forwardedMessages, MamFinIQ mamFin, DataForm form) { + this.forwardedMessages = forwardedMessages; + this.mamFin = mamFin; + this.form = form; + } + } + + /** + * Returns true if Message Archive Management is supported by the server. + * + * @return true if Message Archive Management is supported by the server. + * @throws NotConnectedException + * @throws XMPPErrorException + * @throws NoResponseException + * @throws InterruptedException + */ + public boolean isSupportedByServer() + throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + return ServiceDiscoveryManager.getInstanceFor(connection()).serverSupportsFeature(MamElements.NAMESPACE); + } + + private static DataForm getNewMamForm() { + FormField field = new FormField(FormField.FORM_TYPE); + field.setType(FormField.Type.hidden); + field.addValue(MamElements.NAMESPACE); + DataForm form = new DataForm(DataForm.Type.submit); + form.addField(field); + return form; + } + + /** + * Get the preferences stored in the server. + * + * @return the MAM preferences result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamPrefsResult retrieveArchivingPreferences() throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(); + return queryMamPrefs(mamPrefIQ); + } + + /** + * Update the preferences in the server. + * + * @param alwaysJids + * is the list of JIDs that should always have messages to/from + * archived in the user's store + * @param neverJids + * is the list of JIDs that should never have messages to/from + * archived in the user's store + * @param defaultBehavior + * can be "roster", "always", "never" (see XEP-0313) + * @return the MAM preferences result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamPrefsResult updateArchivingPreferences(List alwaysJids, List neverJids, DefaultBehavior defaultBehavior) + throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, + NotLoggedInException { + Objects.requireNonNull(defaultBehavior, "Default behavior must be set"); + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior); + return queryMamPrefs(mamPrefIQ); + } + + /** + * MAM preferences result class. + * + */ + public final static class MamPrefsResult { + public final MamPrefsIQ mamPrefs; + public final DataForm form; + + private MamPrefsResult(MamPrefsIQ mamPrefs, DataForm form) { + this.mamPrefs = mamPrefs; + this.form = form; + } + } + + private MamPrefsResult queryMamPrefs(MamPrefsIQ mamPrefsIQ) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + final XMPPConnection connection = getAuthenticatedConnectionOrThrow(); + + MamPrefsIQ mamPrefsResultIQ = connection.createPacketCollectorAndSend(mamPrefsIQ).nextResultOrThrow(); + + return new MamPrefsResult(mamPrefsResultIQ, DataForm.from(mamPrefsIQ)); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java new file mode 100644 index 0000000000..8c8eecfe15 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java @@ -0,0 +1,215 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam.element; + +import java.util.List; + +import org.jivesoftware.smack.packet.Element; +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jxmpp.jid.Jid; + +/** + * MAM elements. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez and Florian Schmaus + * + */ +public class MamElements { + + public static final String NAMESPACE = "urn:xmpp:mam:1"; + + /** + * MAM result extension class. + * + * @see XEP-0313: Message + * Archive Management + * + */ + public static class MamResultExtension implements ExtensionElement { + + /** + * result element. + */ + public static final String ELEMENT = "result"; + + /** + * id of the result. + */ + private final String id; + + /** + * the forwarded element. + */ + private final Forwarded forwarded; + + /** + * the query id. + */ + private String queryId; + + /** + * MAM result extension constructor. + * + * @param queryId + * @param id + * @param forwarded + */ + public MamResultExtension(String queryId, String id, Forwarded forwarded) { + if (StringUtils.isEmpty(id)) { + throw new IllegalArgumentException("id must not be null or empty"); + } + if (forwarded == null) { + throw new IllegalArgumentException("forwarded must no be null"); + } + this.id = id; + this.forwarded = forwarded; + this.queryId = queryId; + } + + /** + * Get the id. + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * Get the forwarded element. + * + * @return the forwarded element + */ + public Forwarded getForwarded() { + return forwarded; + } + + /** + * Get query id. + * + * @return the query id + */ + public final String getQueryId() { + return queryId; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public final String getNamespace() { + return NAMESPACE; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(); + xml.halfOpenElement(this); + xml.xmlnsAttribute(NAMESPACE); + xml.optAttribute("queryid", getQueryId()); + xml.optAttribute("id", getId()); + xml.rightAngleBracket(); + + xml.element(getForwarded()); + + xml.closeElement(this); + return xml; + } + + public static MamResultExtension from(Message message) { + return (MamResultExtension) message.getExtension(ELEMENT, NAMESPACE); + } + + } + + /** + * Always JID list element class for the MamPrefsIQ. + * + */ + public static class AlwaysJidListElement implements Element { + + /** + * list of JIDs. + */ + private final List alwaysJids; + + /** + * Always JID list element constructor. + * + * @param alwaysJids + */ + AlwaysJidListElement(List alwaysJids) { + this.alwaysJids = alwaysJids; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(); + xml.openElement("always"); + + for (Jid jid : alwaysJids) { + xml.element("jid", jid); + } + + xml.closeElement("always"); + return xml; + } + } + + /** + * Never JID list element class for the MamPrefsIQ. + * + */ + public static class NeverJidListElement implements Element { + + /** + * list of JIDs + */ + private List neverJids; + + /** + * Never JID list element constructor. + * + * @param neverJids + */ + public NeverJidListElement(List neverJids) { + this.neverJids = neverJids; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(); + xml.openElement("never"); + + for (Jid jid : neverJids) { + xml.element("jid", jid); + } + + xml.closeElement("never"); + return xml; + } + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java new file mode 100644 index 0000000000..4b8a61de87 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java @@ -0,0 +1,131 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * 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.mam.element; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.rsm.packet.RSMSet; + +/** + * MAM fin IQ class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamFinIQ extends IQ { + + /** + * fin element. + */ + public static final String ELEMENT = "fin"; + + /** + * the IQ NAMESPACE. + */ + public static final String NAMESPACE = MamElements.NAMESPACE; + + /** + * RSM set. + */ + private final RSMSet rsmSet; + + /** + * if is complete. + */ + private final boolean complete; + + /** + * if is stable. + */ + private final boolean stable; + + /** + * the query id. + */ + private final String queryId; + + /** + * MamFinIQ constructor. + * + * @param queryId + * @param rsmSet + * @param complete + * @param stable + */ + public MamFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) { + super(ELEMENT, NAMESPACE); + if (rsmSet == null) { + throw new IllegalArgumentException("rsmSet must not be null"); + } + this.rsmSet = rsmSet; + this.complete = complete; + this.stable = stable; + this.queryId = queryId; + } + + /** + * Get RSM set. + * + * @return the RSM set + */ + public RSMSet getRSMSet() { + return rsmSet; + } + + /** + * Return if it is complete. + * + * @return true if it is complete + */ + public boolean isComplete() { + return complete; + } + + /** + * Return if it is stable. + * + * @return true if it is stable + */ + public boolean isStable() { + return stable; + } + + /** + * Get query id. + * + * @return the query id + */ + public final String getQueryId() { + return queryId; + } + + @Override + protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { + xml.optAttribute("queryid", queryId); + xml.optBooleanAttribute("complete", complete); + xml.optBooleanAttribute("stable", stable); + if (rsmSet == null) { + xml.setEmptyElement(); + } else { + xml.rightAngleBracket(); + xml.element(rsmSet); + } + return xml; + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java new file mode 100644 index 0000000000..213737479c --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java @@ -0,0 +1,147 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam.element; + +import java.util.List; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.mam.element.MamElements.AlwaysJidListElement; +import org.jivesoftware.smackx.mam.element.MamElements.NeverJidListElement; +import org.jxmpp.jid.Jid; + +/** + * MAM Preferences IQ class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez and Florian Schmaus + * + */ +public class MamPrefsIQ extends IQ { + + public enum DefaultBehavior { + always, + never, + roster, + ; + } + + /** + * the preferences element. + */ + public static final String ELEMENT = "prefs"; + + /** + * the IQ NAMESPACE. + */ + public static final String NAMESPACE = MamElements.NAMESPACE; + + /** + * list of always. + */ + private final List alwaysJids; + + /** + * list of never. + */ + private final List neverJids; + + /** + * default field. + */ + private final DefaultBehavior defaultBehavior; + + /** + * Construct a new MAM {@code } IQ retrieval request (IQ type 'get'). + */ + public MamPrefsIQ() { + super(ELEMENT, NAMESPACE); + alwaysJids = null; + neverJids = null; + defaultBehavior = null; + } + + /** + * MAM preferences IQ constructor. + * + * @param alwaysJids + * @param neverJids + * @param defaultBehavior + */ + public MamPrefsIQ(List alwaysJids, List neverJids, DefaultBehavior defaultBehavior) { + super(ELEMENT, NAMESPACE); + setType(Type.set); + this.alwaysJids = alwaysJids; + this.neverJids = neverJids; + this.defaultBehavior = defaultBehavior; + } + + /** + * Get the list of always store info JIDs. + * + * @return the always list + */ + public List getAlwaysJids() { + return alwaysJids; + } + + /** + * Get the list of never store info JIDs. + * + * @return the never list + */ + public List getNeverJids() { + return neverJids; + } + + /** + * Get the default behavior. + * + * @return the default behavior. + */ + public DefaultBehavior getDefault() { + return defaultBehavior; + } + + @Override + protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { + + if (getType().equals(IQ.Type.set) || getType().equals(IQ.Type.result)) { + xml.attribute("default", defaultBehavior); + } + + if (alwaysJids == null && neverJids == null) { + xml.setEmptyElement(); + return xml; + } + + xml.rightAngleBracket(); + + if (alwaysJids != null) { + MamElements.AlwaysJidListElement alwaysElement = new AlwaysJidListElement(alwaysJids); + xml.element(alwaysElement); + } + + if (neverJids != null) { + MamElements.NeverJidListElement neverElement = new NeverJidListElement(neverJids); + xml.element(neverElement); + } + + return xml; + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java new file mode 100644 index 0000000000..9a0c190457 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java @@ -0,0 +1,128 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam.element; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +/** + * MAM Query IQ class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez and Florian Schmaus + * + */ +public class MamQueryIQ extends IQ { + + /** + * the MAM query IQ element. + */ + public static final String ELEMENT = QUERY_ELEMENT; + + /** + * the MAM query IQ NAMESPACE. + */ + public static final String NAMESPACE = MamElements.NAMESPACE; + + private final String queryId; + private final String node; + private final DataForm dataForm; + + /** + * MAM query IQ constructor. + * + * @param queryId + */ + public MamQueryIQ(String queryId) { + this(queryId, null, null); + setType(IQ.Type.get); + } + + /** + * MAM query IQ constructor. + * + * @param form + */ + public MamQueryIQ(DataForm form) { + this(null, null, form); + } + + /** + * MAM query IQ constructor. + * + * @param queryId + * @param form + */ + public MamQueryIQ(String queryId, DataForm form) { + this(queryId, null, form); + } + + /** + * MAM query IQ constructor. + * + * @param queryId + * @param node + * @param dataForm + */ + public MamQueryIQ(String queryId, String node, DataForm dataForm) { + super(ELEMENT, NAMESPACE); + this.queryId = queryId; + this.node = node; + this.dataForm = dataForm; + + if (dataForm != null) { + FormField field = dataForm.getHiddenFormTypeField(); + if (field == null) { + throw new IllegalArgumentException("If a data form is given it must posses a hidden form type field"); + } + if (!field.getValues().get(0).equals(MamElements.NAMESPACE)) { + throw new IllegalArgumentException( + "Value of the hidden form type field must be '" + MamElements.NAMESPACE + "'"); + } + addExtension(dataForm); + } + } + + /** + * Get query id. + * + * @return the query id + */ + public String getQueryId() { + return queryId; + } + + /** + * Get the data form. + * + * @return the data form + */ + public DataForm getDataForm() { + return dataForm; + } + + @Override + protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { + xml.optAttribute("queryid", queryId); + xml.optAttribute("node", node); + xml.rightAngleBracket(); + return xml; + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/package-info.java new file mode 100644 index 0000000000..5c63d8c7cf --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ + +/** + * Packet classes and interfaces for Message Archive Management (MAM) XEP-0313. + * + * @see XEP-0313: Message + * Archive Management + * + */ +package org.jivesoftware.smackx.mam.element; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java new file mode 100644 index 0000000000..8dcd318970 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java @@ -0,0 +1,53 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam.filter; + +import org.jivesoftware.smack.filter.FlexibleStanzaTypeFilter; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; + +/** + * MAM result filter class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez and Florian Schmaus + * + */ +public class MamResultFilter extends FlexibleStanzaTypeFilter { + + private final String queryId; + + public MamResultFilter(MamQueryIQ mamQueryIQ) { + super(Message.class); + this.queryId = mamQueryIQ.getQueryId(); + } + + @Override + protected boolean acceptSpecific(Message message) { + MamResultExtension mamResultExtension = MamResultExtension.from(message); + + if (mamResultExtension == null) { + return false; + } + + String resultQueryId = mamResultExtension.getQueryId(); + return ((queryId == null && resultQueryId == null) || (queryId != null && queryId.equals(resultQueryId))); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/package-info.java new file mode 100644 index 0000000000..d568f7d04c --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ + +/** + * Filters of Message Archive Management (MAM) XEP-0313. + * + * @see XEP-0313: Message + * Archive Management + * + */ +package org.jivesoftware.smackx.mam.filter; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/package-info.java new file mode 100644 index 0000000000..16b8b2b7b7 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ + +/** + * XEP-0313: Message Archive Management. + * + * @see XEP-0313: Message + * Archive Management + * + */ +package org.jivesoftware.smackx.mam; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java new file mode 100644 index 0000000000..dbdd4942b5 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java @@ -0,0 +1,62 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam.provider; + +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.util.ParserUtils; +import org.jivesoftware.smackx.mam.element.MamFinIQ; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.jivesoftware.smackx.rsm.provider.RSMSetProvider; +import org.xmlpull.v1.XmlPullParser; + +/** + * MAM Fin IQ Provider class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamFinIQProvider extends IQProvider { + + @Override + public MamFinIQ parse(XmlPullParser parser, int initialDepth) throws Exception { + String queryId = parser.getAttributeValue("", "queryid"); + boolean complete = ParserUtils.getBooleanAttribute(parser, "complete", false); + boolean stable = ParserUtils.getBooleanAttribute(parser, "stable", true); + RSMSet rsmSet = null; + + outerloop: while (true) { + int eventType = parser.next(); + switch (eventType) { + case XmlPullParser.START_TAG: + if (parser.getName().equals(RSMSet.ELEMENT)) { + rsmSet = RSMSetProvider.INSTANCE.parse(parser); + } + break; + case XmlPullParser.END_TAG: + if (parser.getDepth() == initialDepth) { + break outerloop; + } + break; + } + } + + return new MamFinIQ(queryId, rsmSet, complete, stable); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java new file mode 100644 index 0000000000..e656297801 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java @@ -0,0 +1,110 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam.provider; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; +import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * MAM Preferences IQ Provider class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamPrefsIQProvider extends IQProvider { + + @Override + public MamPrefsIQ parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException { + String iqType = parser.getAttributeValue("", "type"); + String defaultBehaviorString = parser.getAttributeValue("", "default"); + DefaultBehavior defaultBehavior = null; + if (defaultBehaviorString != null) { + defaultBehavior = DefaultBehavior.valueOf(defaultBehaviorString); + } + + if (iqType == null) { + iqType = "result"; + } + + List alwaysJids = null; + List neverJids = null; + + outerloop: while (true) { + final int eventType = parser.next(); + final String name = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + switch (name) { + case "always": + alwaysJids = iterateJids(parser); + break; + case "never": + neverJids = iterateJids(parser); + break; + } + break; + case XmlPullParser.END_TAG: + if (parser.getDepth() == initialDepth) { + break outerloop; + } + break; + } + } + + return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior); + } + + private static List iterateJids(XmlPullParser parser) throws XmlPullParserException, IOException { + List jids = new ArrayList<>(); + + int initialDepth = parser.getDepth(); + + outerloop: while (true) { + final int eventType = parser.next(); + final String name = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + switch (name) { + case "jid": + parser.next(); + jids.add(JidCreate.from(parser.getText())); + break; + } + break; + case XmlPullParser.END_TAG: + if (parser.getDepth() == initialDepth) { + break outerloop; + } + break; + } + } + + return jids; + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java new file mode 100644 index 0000000000..cdb859a04a --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java @@ -0,0 +1,64 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam.provider; + +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.jivesoftware.smackx.xdata.provider.DataFormProvider; +import org.xmlpull.v1.XmlPullParser; + +/** + * MAM Query IQ Provider class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamQueryIQProvider extends IQProvider { + + @Override + public MamQueryIQ parse(XmlPullParser parser, int initialDepth) throws Exception { + DataForm dataForm = null; + String queryId = parser.getAttributeValue("", "queryid"); + String node = parser.getAttributeValue("", "node"); + + outerloop: while (true) { + final int eventType = parser.next(); + final String name = parser.getName(); + + switch (eventType) { + case XmlPullParser.START_TAG: + switch (name) { + case DataForm.ELEMENT: + dataForm = DataFormProvider.INSTANCE.parse(parser); + break; + } + break; + case XmlPullParser.END_TAG: + if (parser.getDepth() == initialDepth) { + break outerloop; + } + break; + } + } + + return new MamQueryIQ(queryId, node, dataForm); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java new file mode 100644 index 0000000000..c3b5be1987 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java @@ -0,0 +1,63 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam.provider; + +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.forward.provider.ForwardedProvider; +import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; +import org.xmlpull.v1.XmlPullParser; + +/** + * MAM Result Provider class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamResultProvider extends ExtensionElementProvider { + + @Override + public MamResultExtension parse(XmlPullParser parser, int initialDepth) throws Exception { + Forwarded forwarded = null; + String queryId = parser.getAttributeValue("", "queryid"); + String id = parser.getAttributeValue("", "id"); + + outerloop: while (true) { + final int eventType = parser.next(); + final String name = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + switch (name) { + case Forwarded.ELEMENT: + forwarded = ForwardedProvider.INSTANCE.parse(parser); + break; + } + break; + case XmlPullParser.END_TAG: + if (parser.getDepth() == initialDepth) { + break outerloop; + } + break; + } + } + + return new MamResultExtension(queryId, id, forwarded); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/package-info.java new file mode 100644 index 0000000000..a6c1a20b4b --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ + +/** + * Provider classes of Message Archive Management (MAM) XEP-0313. + * + * @see XEP-0313: Message + * Archive Management + * + */ +package org.jivesoftware.smackx.mam.provider; diff --git a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers index e4906726ee..145309c84f 100644 --- a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers +++ b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers @@ -52,6 +52,28 @@ org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider + + + prefs + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider + + + query + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamQueryIQProvider + + + fin + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamFinIQProvider + + + result + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamResultProvider + + register diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java new file mode 100644 index 0000000000..c9c4e37a5f --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java @@ -0,0 +1,160 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Assert; +import org.junit.Test; +import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.util.XmppDateTime; + +public class FiltersTest extends MamTest { + + private static String getMamXMemberWith(List fieldsNames, List fieldsValues) { + String xml = "" + "" + "" + + MamElements.NAMESPACE + "" + ""; + + for (int i = 0; i < fieldsNames.size() && i < fieldsValues.size(); i++) { + xml += "" + "" + fieldsValues.get(i) + "" + + ""; + } + + xml += ""; + return xml; + } + + @Test + public void checkStartDateFilter() throws Exception { + Method methodAddStartDateFilter = MamManager.class.getDeclaredMethod("addStart", Date.class, DataForm.class); + methodAddStartDateFilter.setAccessible(true); + + Date date = new Date(); + DataForm dataForm = getNewMamForm(); + methodAddStartDateFilter.invoke(mamManager, date, dataForm); + + List fields = new ArrayList<>(); + fields.add("start"); + List values = new ArrayList<>(); + values.add(XmppDateTime.formatXEP0082Date(date)); + + Assert.assertEquals(dataForm.toXML().toString(), getMamXMemberWith(fields, values)); + } + + @Test + public void checkEndDateFilter() throws Exception { + Method methodAddEndDateFilter = MamManager.class.getDeclaredMethod("addEnd", Date.class, DataForm.class); + methodAddEndDateFilter.setAccessible(true); + + Date date = new Date(); + DataForm dataForm = getNewMamForm(); + methodAddEndDateFilter.invoke(mamManager, date, dataForm); + + List fields = new ArrayList<>(); + fields.add("end"); + List values = new ArrayList<>(); + values.add(XmppDateTime.formatXEP0082Date(date)); + + Assert.assertEquals(dataForm.toXML().toString(), getMamXMemberWith(fields, values)); + } + + @Test + public void checkWithJidFilter() throws Exception { + Method methodAddJidFilter = MamManager.class.getDeclaredMethod("addWithJid", Jid.class, DataForm.class); + methodAddJidFilter.setAccessible(true); + + String jid = "test@jid.com"; + DataForm dataForm = getNewMamForm(); + methodAddJidFilter.invoke(mamManager, JidCreate.from(jid), dataForm); + + List fields = new ArrayList<>(); + fields.add("with"); + List values = new ArrayList<>(); + values.add(jid); + + Assert.assertEquals(dataForm.toXML().toString(), getMamXMemberWith(fields, values)); + } + + @Test + public void checkMultipleFilters() throws Exception { + Method methodAddStartDateFilter = MamManager.class.getDeclaredMethod("addStart", Date.class, DataForm.class); + methodAddStartDateFilter.setAccessible(true); + Method methodAddEndDateFilter = MamManager.class.getDeclaredMethod("addEnd", Date.class, DataForm.class); + methodAddEndDateFilter.setAccessible(true); + Method methodAddJidFilter = MamManager.class.getDeclaredMethod("addWithJid", Jid.class, DataForm.class); + methodAddJidFilter.setAccessible(true); + + DataForm dataForm = getNewMamForm(); + Date date = new Date(); + String dateString = XmppDateTime.formatXEP0082Date(date); + String jid = "test@jid.com"; + + methodAddStartDateFilter.invoke(mamManager, date, dataForm); + methodAddEndDateFilter.invoke(mamManager, date, dataForm); + methodAddJidFilter.invoke(mamManager, JidCreate.from(jid), dataForm); + String dataFormResult = dataForm.toXML().toString(); + + List fields = new ArrayList<>(); + List values = new ArrayList<>(); + + fields.add("start"); + values.add(dateString); + Assert.assertNotEquals(dataFormResult, getMamXMemberWith(fields, values)); + + fields.add("end"); + values.add(dateString); + Assert.assertNotEquals(dataFormResult, getMamXMemberWith(fields, values)); + + fields.clear(); + values.clear(); + + fields.add("start"); + values.add(dateString); + fields.add("with"); + values.add(jid); + Assert.assertNotEquals(dataFormResult, getMamXMemberWith(fields, values)); + + fields.clear(); + values.clear(); + + fields.add("end"); + values.add(dateString); + fields.add("with"); + values.add(jid); + fields.add("start"); + values.add(dateString); + Assert.assertNotEquals(dataFormResult, getMamXMemberWith(fields, values)); + + fields.clear(); + values.clear(); + + fields.add("start"); + values.add(dateString); + fields.add("end"); + values.add(dateString); + fields.add("with"); + values.add(jid); + Assert.assertEquals(dataFormResult, getMamXMemberWith(fields, values)); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java new file mode 100644 index 0000000000..a44be134e1 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java @@ -0,0 +1,74 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.mam.element.MamFinIQ; +import org.jivesoftware.smackx.mam.provider.MamFinIQProvider; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.junit.Assert; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class MamFinProviderTest extends MamTest { + + static final String exmapleMamFinXml = "" + + "" + "10" + "09af3-cc343-b409f" + + "" + ""; + + @Test + public void checkMamFinProvider() throws Exception { + XmlPullParser parser = PacketParserUtils.getParserFor(exmapleMamFinXml); + MamFinIQ mamFinIQ = new MamFinIQProvider().parse(parser); + + Assert.assertFalse(mamFinIQ.isComplete()); + Assert.assertTrue(mamFinIQ.isStable()); + Assert.assertNull(mamFinIQ.getQueryId()); + + RSMSet rsmSet = mamFinIQ.getRSMSet(); + Assert.assertEquals(rsmSet.getAfter(), "09af3-cc343-b409f"); + Assert.assertEquals(rsmSet.getMax(), 10); + } + + @Test + public void checkQueryLimitedResults() throws Exception { + // @formatter:off + final String IQ_LIMITED_RESULTS_EXAMPLE = "" + + "" + + "" + + "23452-4534-1" + + "390-2342-22" + "16" + + "" + + "" + + ""; + // @formatter:on + + IQ iq = (IQ) PacketParserUtils.parseStanza(IQ_LIMITED_RESULTS_EXAMPLE); + + MamFinIQ mamFinIQ = (MamFinIQ) iq; + Assert.assertEquals(mamFinIQ.getType(), Type.result); + + Assert.assertTrue(mamFinIQ.isComplete()); + Assert.assertEquals(mamFinIQ.getRSMSet().getCount(), 16); + Assert.assertEquals(mamFinIQ.getRSMSet().getFirst(), "23452-4534-1"); + Assert.assertEquals(mamFinIQ.getRSMSet().getFirstIndex(), 0); + Assert.assertEquals(mamFinIQ.getRSMSet().getLast(), "390-2342-22"); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java new file mode 100644 index 0000000000..30c270cd97 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java @@ -0,0 +1,84 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.List; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider; +import org.junit.Assert; +import org.junit.Test; +import org.jxmpp.jid.Jid; +import org.xmlpull.v1.XmlPullParser; + +public class MamPrefIQProviderTest extends MamTest { + + String exampleMamPrefsIQ1 = "" + "" + + "" + "romeo@montague.lit" + "" + "" + + "montague@montague.lit" + "" + "" + ""; + + String exampleMamPrefsIQ2 = "" + "" + + "" + "romeo@montague.lit" + "montague@montague.lit" + "" + + "" + "" + "" + ""; + + String exampleMamPrefsIQ3 = "" + "" + "" + + ""; + + String exampleMamPrefsResultIQ = "" + + "" + "" + "romeo@montague.lit" + + "" + "" + "sarasa@montague.lit" + "montague@montague.lit" + + "" + "" + ""; + + @Test + public void checkMamPrefsIQProvider() throws Exception { + XmlPullParser parser1 = PacketParserUtils.getParserFor(exampleMamPrefsIQ1); + MamPrefsIQ mamPrefIQ1 = new MamPrefsIQProvider().parse(parser1); + + Assert.assertEquals(IQ.Type.set, mamPrefIQ1.getType()); + Assert.assertEquals(mamPrefIQ1.getAlwaysJids().get(0), "romeo@montague.lit"); + Assert.assertEquals(mamPrefIQ1.getNeverJids().get(0), "montague@montague.lit"); + + XmlPullParser parser2 = PacketParserUtils.getParserFor(exampleMamPrefsIQ2); + MamPrefsIQ mamPrefIQ2 = new MamPrefsIQProvider().parse(parser2); + Assert.assertEquals(IQ.Type.set, mamPrefIQ2.getType()); + Assert.assertEquals(mamPrefIQ2.getAlwaysJids().get(0), "romeo@montague.lit"); + Assert.assertEquals(mamPrefIQ2.getAlwaysJids().get(1), "montague@montague.lit"); + Assert.assertTrue(mamPrefIQ2.getNeverJids().isEmpty()); + + XmlPullParser parser3 = PacketParserUtils.getParserFor(exampleMamPrefsIQ3); + MamPrefsIQ mamPrefIQ3 = new MamPrefsIQProvider().parse(parser3); + Assert.assertEquals(IQ.Type.set, mamPrefIQ3.getType()); + } + + @Test + public void checkMamPrefResult() throws Exception { + IQ iq = (IQ) PacketParserUtils.parseStanza(exampleMamPrefsResultIQ); + + MamPrefsIQ mamPrefsIQ = (MamPrefsIQ) iq; + + List alwaysJids = mamPrefsIQ.getAlwaysJids(); + List neverJids = mamPrefsIQ.getNeverJids(); + + Assert.assertEquals(alwaysJids.size(), 1); + Assert.assertEquals(neverJids.size(), 2); + Assert.assertEquals(alwaysJids.get(0).toString(), "romeo@montague.lit"); + Assert.assertEquals(neverJids.get(1).toString(), "montague@montague.lit"); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java new file mode 100644 index 0000000000..9141137c37 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java @@ -0,0 +1,86 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Assert; +import org.junit.Test; + +public class MamQueryIQProviderTest { + + String exampleMamQueryIQ1 = "" + "" + + "" + "" + + "urn:xmpp:mam:1" + "" + + "" + + "Where arth thou, my Juliet?" + "" + + "" + + "{http://jabber.org/protocol/mood}mood/lonely" + "" + "" + "" + + ""; + + String exampleMamQueryIQ2 = "" + "" + + "" + "" + + "urn:xmpp:mam:1" + "" + "" + + "" + "" + + "" + + "" + "" + "" + ""; + + @Test + public void checkMamQueryIQProvider() throws Exception { + // example 1 + IQ iq1 = (IQ) PacketParserUtils.parseStanza(exampleMamQueryIQ1); + MamQueryIQ mamQueryIQ1 = (MamQueryIQ) iq1; + + Assert.assertEquals(mamQueryIQ1.getType(), Type.set); + Assert.assertEquals(mamQueryIQ1.getQueryId(), "test"); + + DataForm dataForm1 = (DataForm) mamQueryIQ1.getExtension(DataForm.NAMESPACE); + Assert.assertEquals(dataForm1.getType(), DataForm.Type.submit); + + List fields1 = dataForm1.getFields(); + Assert.assertEquals(fields1.get(0).getType(), FormField.Type.hidden); + Assert.assertEquals(fields1.get(1).getType(), FormField.Type.text_single); + Assert.assertEquals(fields1.get(1).getValues().get(0), "Where arth thou, my Juliet?"); + Assert.assertEquals(fields1.get(2).getValues().get(0), "{http://jabber.org/protocol/mood}mood/lonely"); + + // example2 + IQ iq2 = (IQ) PacketParserUtils.parseStanza(exampleMamQueryIQ2); + MamQueryIQ mamQueryIQ2 = (MamQueryIQ) iq2; + + Assert.assertEquals(mamQueryIQ2.getType(), Type.result); + Assert.assertNull(mamQueryIQ2.getQueryId()); + + DataForm dataForm2 = (DataForm) mamQueryIQ2.getExtension(DataForm.NAMESPACE); + Assert.assertEquals(dataForm2.getType(), DataForm.Type.form); + + List fields2 = dataForm2.getFields(); + Assert.assertEquals(fields2.get(0).getValues().get(0), "urn:xmpp:mam:1"); + Assert.assertTrue(fields2.get(0).getValues().size() == 1); + Assert.assertEquals(fields2.get(1).getType(), FormField.Type.jid_single); + Assert.assertEquals(fields2.get(2).getType(), FormField.Type.text_single); + Assert.assertEquals(fields2.get(2).getValues(), new ArrayList<>()); + Assert.assertEquals(fields2.get(4).getVariable(), "urn:example:xmpp:free-text-search"); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java new file mode 100644 index 0000000000..c8c43eaea2 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java @@ -0,0 +1,90 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; +import org.jivesoftware.smackx.mam.provider.MamResultProvider; +import org.junit.Assert; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class MamResultProviderTest { + + String exampleMamResultXml = "" + + "" + "" + + "" + + "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo." + + "" + "" + ""; + + String exampleResultMessage = "" + + "" + + "" + "" + + "" + + "Hail to thee" + "" + "" + "" + ""; + + @Test + public void checkMamResultProvider() throws Exception { + XmlPullParser parser = PacketParserUtils.getParserFor(exampleMamResultXml); + MamResultExtension mamResultExtension = new MamResultProvider().parse(parser); + + Assert.assertEquals(mamResultExtension.getQueryId(), "f27"); + Assert.assertEquals(mamResultExtension.getId(), "28482-98726-73623"); + + GregorianCalendar calendar = new GregorianCalendar(2010, 7 - 1, 10, 23, 8, 25); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = calendar.getTime(); + + Forwarded forwarded = mamResultExtension.getForwarded(); + Assert.assertEquals(forwarded.getDelayInformation().getStamp(), date); + + Message message = (Message) forwarded.getForwardedStanza(); + Assert.assertEquals(message.getFrom(), "romeo@montague.lit/orchard"); + Assert.assertEquals(message.getTo(), "juliet@capulet.lit/balcony"); + Assert.assertEquals(message.getBody(), + "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo."); + } + + @Test + public void checkResultsParse() throws Exception { + Message message = (Message) PacketParserUtils.parseStanza(exampleResultMessage); + MamResultExtension mamResultExtension = MamResultExtension.from(message); + + Assert.assertEquals(mamResultExtension.getQueryId(), "f27"); + Assert.assertEquals(mamResultExtension.getId(), "28482-98726-73623"); + + GregorianCalendar calendar = new GregorianCalendar(2010, 7 - 1, 10, 23, 8, 25); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = calendar.getTime(); + + Forwarded forwarded = mamResultExtension.getForwarded(); + Assert.assertEquals(forwarded.getDelayInformation().getStamp(), date); + + Message forwardedMessage = (Message) forwarded.getForwardedStanza(); + Assert.assertEquals(forwardedMessage.getFrom(), "witch@shakespeare.lit"); + Assert.assertEquals(forwardedMessage.getTo(), "macbeth@shakespeare.lit"); + Assert.assertEquals(forwardedMessage.getBody(), "Hail to thee"); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java new file mode 100644 index 0000000000..6d7c7c46ee --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java @@ -0,0 +1,54 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import static org.mockito.Mockito.mock; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smackx.ExperimentalInitializerTest; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Before; + +public class MamTest extends ExperimentalInitializerTest { + + protected XMPPConnection connection; + protected String queryId; + protected MamManager mamManager; + + @Before + public void setup() { + // mock connection + connection = mock(XMPPConnection.class); + + // test query id + queryId = "testid"; + + // MamManager instance + mamManager = MamManager.getInstanceFor(connection); + } + + protected DataForm getNewMamForm() throws NoSuchMethodException, SecurityException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException { + Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm"); + methodGetNewMamForm.setAccessible(true); + return (DataForm) methodGetNewMamForm.invoke(mamManager); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java new file mode 100644 index 0000000000..ad32e32d51 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Test; + +import org.junit.Assert; + +public class PagingTest extends MamTest { + + private static final String pagingStanza = "" + "" + + "" + "" + + "urn:xmpp:mam:1" + "" + "" + "" + + "10" + "" + "" + ""; + + @Test + public void checkPageQueryStanza() throws Exception { + DataForm dataForm = getNewMamForm(); + int max = 10; + RSMSet rsmSet = new RSMSet(max); + + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + mamQueryIQ.setStanzaId("sarasa"); + mamQueryIQ.setType(IQ.Type.set); + mamQueryIQ.addExtension(rsmSet); + + Assert.assertEquals(mamQueryIQ.getDataForm(), dataForm); + Assert.assertEquals(mamQueryIQ.getDataForm().getFields().get(0).getValues().get(0), "urn:xmpp:mam:1"); + Assert.assertEquals(mamQueryIQ.toXML().toString(), pagingStanza); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java new file mode 100644 index 0000000000..3ce06cc816 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java @@ -0,0 +1,60 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; +import org.junit.Assert; + +public class PreferencesTest { + + String retrievePrefsStanzaExample = "" + "" + ""; + + String updatePrefsStanzaExample = "" + "" + "" + "romeo@montague.lit" + "other@montague.lit" + + "" + "" + "montague@montague.lit" + "" + "" + ""; + + @Test + public void checkRetrievePrefsStanza() throws Exception { + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(); + mamPrefIQ.setStanzaId("sarasa"); + Assert.assertEquals(mamPrefIQ.toXML().toString(), retrievePrefsStanzaExample); + } + + @Test + public void checkUpdatePrefsStanza() throws Exception { + List alwaysJids = new ArrayList<>(); + alwaysJids.add(JidCreate.from("romeo@montague.lit")); + alwaysJids.add(JidCreate.from("other@montague.lit")); + + List neverJids = new ArrayList<>(); + neverJids.add(JidCreate.from("montague@montague.lit")); + + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster); + mamPrefIQ.setStanzaId("sarasa"); + Assert.assertEquals(mamPrefIQ.toXML().toString(), updatePrefsStanzaExample); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java new file mode 100644 index 0000000000..936aa33e14 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java @@ -0,0 +1,95 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Message.Type; +import org.jivesoftware.smackx.delay.packet.DelayInformation; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Assert; +import org.junit.Test; +import org.jxmpp.jid.impl.JidCreate; + +public class QueryArchiveTest extends MamTest { + + String mamSimpleQueryIQ = "" + "" + + "" + "" + "" + + MamElements.NAMESPACE + "" + "" + "" + "" + ""; + + String mamQueryResultExample = "" + + "" + + "" + + "" + "" + "Thrice the brinded cat hath mew." + "" + "" + + "" + ""; + + @Test + public void checkMamQueryIQ() throws Exception { + DataForm dataForm = getNewMamForm(); + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + mamQueryIQ.setType(IQ.Type.set); + mamQueryIQ.setStanzaId("sarasa"); + Assert.assertEquals(mamQueryIQ.toXML().toString(), mamSimpleQueryIQ); + } + + @Test + public void checkMamQueryResults() throws Exception { + Message message = new Message(); + message.setStanzaId("iasd207"); + message.setFrom(JidCreate.from("coven@chat.shakespeare.lit")); + message.setTo(JidCreate.from("hag66@shakespeare.lit/pda")); + + GregorianCalendar calendar = new GregorianCalendar(2002, 10 - 1, 13, 23, 58, 37); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = calendar.getTime(); + + DelayInformation delay = new DelayInformation(date); + Message forwardedMessage = new Message(); + forwardedMessage.setFrom(JidCreate.from("coven@chat.shakespeare.lit/firstwitch")); + forwardedMessage.setStanzaId("162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2"); + forwardedMessage.setType(Type.chat); + forwardedMessage.setBody("Thrice the brinded cat hath mew."); + + Forwarded forwarded = new Forwarded(delay, forwardedMessage); + + message.addExtension(new MamResultExtension("g27", "34482-21985-73620", forwarded)); + + Assert.assertEquals(message.toXML().toString(), mamQueryResultExample); + + MamResultExtension mamResultExtension = MamResultExtension.from(message); + + Assert.assertEquals(mamResultExtension.getId(), "34482-21985-73620"); + Assert.assertEquals(mamResultExtension.getForwarded().getDelayInformation().getStamp(), date); + + Message resultMessage = (Message) mamResultExtension.getForwarded().getForwardedStanza(); + Assert.assertEquals(resultMessage.getFrom(), JidCreate.from("coven@chat.shakespeare.lit/firstwitch")); + Assert.assertEquals(resultMessage.getStanzaId(), "162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2"); + Assert.assertEquals(resultMessage.getType(), Type.chat); + Assert.assertEquals(resultMessage.getBody(), "Thrice the brinded cat hath mew."); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java new file mode 100644 index 0000000000..f932666d3c --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.lang.reflect.Method; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Assert; +import org.junit.Test; + +public class ResultsLimitTest extends MamTest { + + String resultsLimitStanza = "" + "" + + "" + "" + "" + + MamElements.NAMESPACE + "" + "" + "" + "" + + "10" + "" + "" + ""; + + @Test + public void checkResultsLimit() throws Exception { + Method methodAddResultsLimit = MamManager.class.getDeclaredMethod("addResultsLimit", Integer.class, + MamQueryIQ.class); + methodAddResultsLimit.setAccessible(true); + + DataForm dataForm = getNewMamForm(); + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + mamQueryIQ.setType(IQ.Type.set); + mamQueryIQ.setStanzaId("sarasa"); + + methodAddResultsLimit.invoke(mamManager, 10, mamQueryIQ); + Assert.assertEquals(mamQueryIQ.toXML().toString(), resultsLimitStanza); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java new file mode 100644 index 0000000000..46939c42d3 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java @@ -0,0 +1,78 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Test; + +import org.junit.Assert; + +public class RetrieveFormFieldsTest extends MamTest { + + private static final String retrieveFormFieldStanza = "" + "" + ""; + + private static final String additionalFieldsStanza = "" + "" + + "" + MamElements.NAMESPACE + "" + "" + + "" + "Hi" + "" + + "" + "Hi2" + "" + + ""; + + @Test + public void checkRetrieveFormFieldsStanza() throws Exception { + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId); + mamQueryIQ.setStanzaId("sarasa"); + + Assert.assertEquals(mamQueryIQ.toXML().toString(), retrieveFormFieldStanza); + } + + @Test + public void checkAddAdditionalFieldsStanza() throws Exception { + Method methodAddAdditionalFields = MamManager.class.getDeclaredMethod("addAdditionalFields", List.class, + DataForm.class); + methodAddAdditionalFields.setAccessible(true); + + DataForm dataForm = getNewMamForm(); + + List additionalFields = new ArrayList<>(); + + FormField field1 = new FormField("urn:example:xmpp:free-text-search"); + field1.setType(FormField.Type.text_single); + field1.addValue("Hi"); + + FormField field2 = new FormField("urn:example:xmpp:stanza-content"); + field2.setType(FormField.Type.jid_single); + field2.addValue("Hi2"); + + additionalFields.add(field1); + additionalFields.add(field2); + + methodAddAdditionalFields.invoke(mamManager, additionalFields, dataForm); + + String dataFormResult = dataForm.toXML().toString(); + + Assert.assertEquals(dataFormResult, additionalFieldsStanza); + } + +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java index f3564484e1..03a59a93fb 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java @@ -36,6 +36,8 @@ */ public class ForwardedProvider extends ExtensionElementProvider { + public static final ForwardedProvider INSTANCE = new ForwardedProvider(); + private static final Logger LOGGER = Logger.getLogger(ForwardedProvider.class.getName()); @Override diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/offline/OfflineMessageManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/offline/OfflineMessageManager.java index 71ef3bd662..0315868d1f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/offline/OfflineMessageManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/offline/OfflineMessageManager.java @@ -26,6 +26,7 @@ import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter; +import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; @@ -228,6 +229,7 @@ public List getMessages() throws NoResponseException, XMPPErrorExceptio */ public void deleteMessages(List nodes) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { OfflineMessageRequest request = new OfflineMessageRequest(); + request.setType(IQ.Type.set); for (String node : nodes) { OfflineMessageRequest.Item item = new OfflineMessageRequest.Item(node); item.setAction("remove"); @@ -247,6 +249,7 @@ public void deleteMessages(List nodes) throws NoResponseException, XMPPE */ public void deleteMessages() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { OfflineMessageRequest request = new OfflineMessageRequest(); + request.setType(IQ.Type.set); request.setPurge(true); connection.createPacketCollectorAndSend(request).nextResultOrThrow(); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/provider/RSMSetProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/provider/RSMSetProvider.java index 8cc90bb24b..c2bcbf7f19 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/provider/RSMSetProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/provider/RSMSetProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014 Florian Schmaus + * Copyright © 2014-2016 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,8 @@ public class RSMSetProvider extends ExtensionElementProvider { + public static final RSMSetProvider INSTANCE = new RSMSetProvider(); + @Override public RSMSet parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java index cd59cc2e21..aaf04a9b0f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java @@ -40,6 +40,8 @@ */ public class DataFormProvider extends ExtensionElementProvider { + public static final DataFormProvider INSTANCE = new DataFormProvider(); + @Override public DataForm parse(XmlPullParser parser, int initialDepth) throws Exception { diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java index 59513f454c..52a18dad29 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java @@ -199,7 +199,9 @@ private enum RosterState { private SubscriptionMode subscriptionMode = getDefaultSubscriptionMode(); - private SubscribeListener subscribeListener; + private final Set subscribeListeners = new CopyOnWriteArraySet<>(); + + private SubscriptionMode previousSubscriptionMode; /** * Returns the default subscription processing mode to use when a new Roster is created. The @@ -249,11 +251,12 @@ public void processPacket(Stanza stanza) throws NotConnectedException, SubscribeAnswer subscribeAnswer = null; switch (subscriptionMode) { case manual: - final SubscribeListener subscribeListener = Roster.this.subscribeListener; - if (subscribeListener == null) { - return; + for (SubscribeListener subscribeListener : subscribeListeners) { + subscribeAnswer = subscribeListener.processSubscribe(from, presence); + if (subscribeAnswer != null) { + break; + } } - subscribeAnswer = subscribeListener.processSubscribe(from, presence); if (subscribeAnswer == null) { return; } @@ -666,19 +669,37 @@ public void sendSubscriptionRequest(BareJid jid) throws NotLoggedInException, No } /** - * Set the subscribe listener, which is invoked on incoming subscription requests and if - * {@link SubscriptionMode} is set to {@link SubscriptionMode#manual}. If - * subscribeListener is not null, then this also sets subscription + * Add a subscribe listener, which is invoked on incoming subscription requests and if + * {@link SubscriptionMode} is set to {@link SubscriptionMode#manual}. This also sets subscription * mode to {@link SubscriptionMode#manual}. * - * @param subscribeListener the subscribe listener to set. + * @param subscribeListener the subscribe listener to add. + * @return true if the listener was not already added. + * @since 4.2 + */ + public boolean addSubscribeListener(SubscribeListener subscribeListener) { + Objects.requireNonNull(subscribeListener, "SubscribeListener argument must not be null"); + if (subscriptionMode != SubscriptionMode.manual) { + previousSubscriptionMode = subscriptionMode; + } + return subscribeListeners.add(subscribeListener); + } + + /** + * Remove a subscribe listener. Also restores the previous subscription mode + * state, if the last listener got removed. + * + * @param subscribeListener + * the subscribe listener to remove. + * @return true if the listener registered and got removed. * @since 4.2 */ - public void setSubscribeListener(SubscribeListener subscribeListener) { - if (subscribeListener != null) { - setSubscriptionMode(SubscriptionMode.manual); + public boolean removeSubscribeListener(SubscribeListener subscribeListener) { + boolean removed = subscribeListeners.remove(subscribeListener); + if (removed && subscribeListeners.isEmpty()) { + setSubscriptionMode(previousSubscriptionMode); } - this.subscribeListener = subscribeListener; + return removed; } /** diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java index 9f36a4c99c..abc9a4fd22 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java @@ -266,7 +266,11 @@ public static Configuration newConfiguration() throws IOException { String accountTwoPassword = properties.getProperty("accountTwoPassword"); String accountThreeUsername = properties.getProperty("accountThreeUsername"); String accountThreePassword = properties.getProperty("accountThreePassword"); - builder.setUsernamesAndPassword(accountOneUsername, accountOnePassword, accountTwoUsername, accountTwoPassword, accountThreeUsername, accountThreePassword); + if (accountOneUsername != null || accountOnePassword != null || accountTwoUsername != null + || accountTwoPassword != null || accountThreeUsername != null || accountThreePassword != null) { + builder.setUsernamesAndPassword(accountOneUsername, accountOnePassword, accountTwoUsername, + accountTwoPassword, accountThreeUsername, accountThreePassword); + } builder.setDebug(properties.getProperty("debug")); builder.setEnabledTests(properties.getProperty("enabledTests")); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java index d5a6394d30..8691bacaca 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java @@ -50,7 +50,7 @@ public RosterIntegrationTest(SmackIntegrationTestEnvironment environment) { public void subscribeRequestListenerTest() throws TimeoutException, Exception { ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); - rosterTwo.setSubscribeListener(new SubscribeListener() { + final SubscribeListener subscribeListener = new SubscribeListener() { @Override public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { if (from.equals(conOne.getUser().asBareJid())) { @@ -58,7 +58,8 @@ public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { } return SubscribeAnswer.Deny; } - }); + }; + rosterTwo.addSubscribeListener(subscribeListener); final String conTwosRosterName = "ConTwo " + testRunId; final SimpleResultSyncPoint addedAndSubscribed = new SimpleResultSyncPoint(); @@ -88,9 +89,15 @@ private void checkIfAddedAndSubscribed(Collection addresses) { } } }); - rosterOne.createEntry(conTwo.getUser().asBareJid(), conTwosRosterName, null); - assertTrue(addedAndSubscribed.waitForResult(2 * connection.getPacketReplyTimeout())); + try { + rosterOne.createEntry(conTwo.getUser().asBareJid(), conTwosRosterName, null); + + assertTrue(addedAndSubscribed.waitForResult(2 * connection.getPacketReplyTimeout())); + } + finally { + rosterTwo.removeSubscribeListener(subscribeListener); + } } public static void ensureBothAccountsAreNotInEachOthersRoster(XMPPConnection conOne, XMPPConnection conTwo) throws NotLoggedInException, @@ -124,7 +131,7 @@ private static void ensureSubscribedTo(final XMPPConnection conOne, final XMPPCo return; } - rosterOne.setSubscribeListener(new SubscribeListener() { + final SubscribeListener subscribeListener = new SubscribeListener() { @Override public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { if (from.equals(conTwo.getUser().asBareJid())) { @@ -132,7 +139,8 @@ public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { } return SubscribeAnswer.Deny; } - }); + }; + rosterOne.addSubscribeListener(subscribeListener); final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint(); rosterTwo.addPresenceEventListener(new AbstractPresenceEventListener() { @@ -149,7 +157,7 @@ public void presenceSubscribed(BareJid address, Presence subscribedPresence) { try { syncPoint.waitForResult(timeout); } finally { - rosterOne.setSubscribeListener(null); + rosterOne.removeSubscribeListener(subscribeListener); } } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java new file mode 100644 index 0000000000..c1ad1dce61 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java @@ -0,0 +1,81 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; +import org.igniterealtime.smack.inttest.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.TestNotPossibleException; +import org.jivesoftware.smack.SmackException.NoResponseException; +import org.jivesoftware.smack.SmackException.NotConnectedException; +import org.jivesoftware.smack.SmackException.NotLoggedInException; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.mam.MamManager.MamQueryResult; +import org.jxmpp.jid.EntityBareJid; + +public class MamIntegrationTest extends AbstractSmackIntegrationTest { + + private final MamManager mamManagerConTwo; + + public MamIntegrationTest(SmackIntegrationTestEnvironment environment) throws NoResponseException, + XMPPErrorException, NotConnectedException, InterruptedException, TestNotPossibleException { + super(environment); + + mamManagerConTwo = MamManager.getInstanceFor(conTwo); + + if (!mamManagerConTwo.isSupportedByServer()) { + throw new TestNotPossibleException("Message Archive Management (XEP-0313) is not supported by the server."); + } + + } + + @SmackIntegrationTest + public void mamTest() throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException, + NotLoggedInException { + EntityBareJid userOne = conOne.getUser().asEntityBareJid(); + EntityBareJid userTwo = conTwo.getUser().asEntityBareJid(); + + Message message = new Message(userTwo); + String messageId = message.setStanzaId(); + String messageBody = "test message"; + message.setBody(messageBody); + + conOne.sendStanza(message); + + int pageSize = 20; + MamQueryResult mamQueryResult = mamManagerConTwo.queryArchive(pageSize, null, null, userOne, null); + + while (!mamQueryResult.mamFin.isComplete()) { + mamQueryResult = mamManagerConTwo.pageNext(mamQueryResult, pageSize); + } + + List forwardedMessages = mamQueryResult.forwardedMessages; + Message mamMessage = (Message) forwardedMessages.get(forwardedMessages.size() - 1).getForwardedStanza(); + + assertEquals(messageId, mamMessage.getStanzaId()); + assertEquals(messageBody, mamMessage.getBody()); + assertEquals(conOne.getUser(), mamMessage.getFrom()); + assertEquals(userTwo, mamMessage.getTo()); + } + +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java new file mode 120000 index 0000000000..54c99cb446 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java @@ -0,0 +1 @@ +../../../../../../../../smack-experimental/src/main/java/org/jivesoftware/smackx/mam/package-info.java \ No newline at end of file diff --git a/version.gradle b/version.gradle index e74f1891ad..16b0168971 100644 --- a/version.gradle +++ b/version.gradle @@ -1,6 +1,6 @@ allprojects { ext { - shortVersion = '4.2.0-beta2' + shortVersion = '4.2.0-beta3' isSnapshot = true jxmppVersion = '0.5.0-alpha7' smackMinAndroidSdk = 8