1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-12-08 14:11:07 +01:00

Implement Message Archive Management (MAM) XEP-0313

Fixes SMACK-625
This commit is contained in:
Fernando Ramirez 2016-07-21 11:40:26 -03:00 committed by Florian Schmaus
parent b91978dcc4
commit 189cac072b
29 changed files with 2637 additions and 0 deletions

View file

@ -0,0 +1,561 @@
/**
*
* 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.IQ.Type;
import org.jivesoftware.smack.packet.Message;
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.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;
/**
* Message Archive Management Manager class.
*
* @see <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @author Fernando Ramirez and Florian Schmaus
*
*/
public final class MamManager extends Manager {
static {
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
@Override
public void connectionCreated(XMPPConnection connection) {
getInstanceFor(connection);
}
});
}
private static final Map<XMPPConnection, MamManager> 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<FormField> 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<FormField> 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 = prepareMamQueryIQSet(dataForm, queryId);
addResultsLimit(max, mamQueryIQ);
return queryArchive(mamQueryIQ);
}
private static void addAdditionalFields(List<FormField> 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);
}
private void preparePageQuery(MamQueryIQ mamQueryIQ, RSMSet rsmSet) {
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.addExtension(rsmSet);
}
/**
* 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);
preparePageQuery(mamQueryIQ, 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<FormField> retrieveFormFields() throws NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException, NotLoggedInException {
String queryId = UUID.randomUUID().toString();
MamQueryIQ mamQueryIQ = prepareMamQueryIQGet(queryId);
return queryFormFields(mamQueryIQ);
}
private MamQueryIQ prepareMamQueryIQSet(DataForm dataForm, String queryId) {
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
mamQueryIQ.setType(IQ.Type.set);
return mamQueryIQ;
}
private MamQueryIQ prepareMamQueryIQGet(String queryId) {
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, null);
mamQueryIQ.setType(IQ.Type.get);
return mamQueryIQ;
}
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<Forwarded> 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<Forwarded> forwardedMessages;
public final MamFinIQ mamFin;
private final DataForm form;
private MamQueryResult(List<Forwarded> forwardedMessages, MamFinIQ mamFin, DataForm form) {
this.forwardedMessages = forwardedMessages;
this.mamFin = mamFin;
this.form = form;
}
}
private List<FormField> queryFormFields(MamQueryIQ mamQueryIq) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
final XMPPConnection connection = connection();
MamQueryIQ mamResponseQueryIQ = null;
PacketCollector mamResponseQueryIQCollector = connection
.createPacketCollector(new IQReplyFilter(mamQueryIq, connection));
try {
connection.sendStanza(mamQueryIq);
mamResponseQueryIQ = mamResponseQueryIQCollector.nextResultOrThrow();
} finally {
mamResponseQueryIQCollector.cancel();
}
return mamResponseQueryIQ.getDataForm().getFields();
}
/**
* 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 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 = prepareRetrievePreferencesStanza();
return queryMamPrefs(mamPrefIQ);
}
private MamPrefsIQ prepareRetrievePreferencesStanza() {
MamPrefsIQ mamPrefIQ = new MamPrefsIQ(Type.get, null, null, null);
return 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 defaultField
* can be "roster", "always", "never" (look at the XEP-0313
* documentation)
* @return the MAM preferences result
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
* @throws NotLoggedInException
*/
public MamPrefsResult updateArchivingPreferences(List<Jid> alwaysJids, List<Jid> neverJids, String defaultField)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException,
NotLoggedInException {
MamPrefsIQ mamPrefIQ = prepareUpdatePreferencesStanza(alwaysJids, neverJids, defaultField);
return queryMamPrefs(mamPrefIQ);
}
private MamPrefsIQ prepareUpdatePreferencesStanza(List<Jid> alwaysJids, List<Jid> neverJids, String defaultField) {
MamPrefsIQ mamPrefIQ = new MamPrefsIQ(Type.set, alwaysJids, neverJids, defaultField);
return 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 = null;
PacketCollector prefsResultIQCollector = connection
.createPacketCollector(new IQReplyFilter(mamPrefsIQ, connection));
try {
connection.sendStanza(mamPrefsIQ);
mamPrefsResultIQ = prefsResultIQCollector.nextResultOrThrow();
} finally {
prefsResultIQCollector.cancel();
}
return new MamPrefsResult(mamPrefsResultIQ, DataForm.from(mamPrefsIQ));
}
}

View file

@ -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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @author Fernando Ramirez and Florian Schmaus
*
*/
public class MamElements {
public static final String NAMESPACE = "urn:xmpp:mam:1";
/**
* MAM result extension class.
*
* @see <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
*
*/
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 List<Jid> alwaysJids;
/**
* Always JID list element constructor.
*
* @param alwaysJids
*/
AlwaysJidListElement(List<Jid> 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<Jid> neverJids;
/**
* Never JID list element constructor.
*
* @param neverJids
*/
public NeverJidListElement(List<Jid> 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;
}
}
}

View file

@ -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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @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;
}
}

View file

@ -0,0 +1,156 @@
/**
*
* 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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @author Fernando Ramirez and Florian Schmaus
*
*/
public class MamPrefsIQ extends IQ {
/**
* the preferences element.
*/
public static final String ELEMENT = "prefs";
/**
* the IQ NAMESPACE.
*/
public static final String NAMESPACE = MamElements.NAMESPACE;
/**
* true if it is a request for update preferences.
*/
private boolean isUpdate;
/**
* true if it is a result preferences.
*/
private boolean isResult;
/**
* list of always.
*/
private List<Jid> alwaysJids;
/**
* list of never.
*/
private List<Jid> neverJids;
/**
* default field.
*/
private String defaultField;
/**
* MAM preferences IQ constructor.
*
* @param type
* @param alwaysJids
* @param neverJids
* @param defaultField
*/
public MamPrefsIQ(Type type, List<Jid> alwaysJids, List<Jid> neverJids, String defaultField) {
super(ELEMENT, NAMESPACE);
this.setType(type);
this.isUpdate = this.getType().equals(Type.set);
this.isResult = this.getType().equals(Type.result);
this.alwaysJids = alwaysJids;
this.neverJids = neverJids;
this.defaultField = defaultField;
}
/**
* True if it is a request for update preferences.
*
* @return the update preferences boolean
*/
public boolean isUpdate() {
return isUpdate;
}
/**
* True if it is a result.
*
* @return the result preferences boolean
*/
public boolean isResult() {
return isUpdate;
}
/**
* Get the list of always store info JIDs.
*
* @return the always list
*/
public List<Jid> getAlwaysJids() {
return alwaysJids;
}
/**
* Get the list of never store info JIDs.
*
* @return the never list
*/
public List<Jid> getNeverJids() {
return neverJids;
}
/**
* Get the default field.
*
* @return the default field
*/
public String getDefault() {
return defaultField;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
if (isUpdate || isResult) {
xml.attribute("default", defaultField);
}
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;
}
}

View file

@ -0,0 +1,127 @@
/**
*
* 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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @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);
}
/**
* 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;
}
}

View file

@ -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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
*
*/
package org.jivesoftware.smackx.mam.element;

View file

@ -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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @author Fernando Ramirez and Florian Schmaus
*
*/
public class MamResultFilter extends FlexibleStanzaTypeFilter<Message> {
private 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)));
}
}

View file

@ -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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
*
*/
package org.jivesoftware.smackx.mam.filter;

View file

@ -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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
*
*/
package org.jivesoftware.smackx.mam;

View file

@ -0,0 +1,58 @@
/**
*
* 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.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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @author Fernando Ramirez
*
*/
public class MamFinIQProvider extends IQProvider<MamFinIQ> {
@Override
public MamFinIQ parse(XmlPullParser parser, int initialDepth) throws Exception {
String queryId = parser.getAttributeValue("", "queryid");
boolean complete = Boolean.parseBoolean(parser.getAttributeValue("", "complete"));
boolean stable = Boolean.parseBoolean(parser.getAttributeValue("", "stable"));
RSMSet rsmSet = null;
outerloop: while (true) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals(RSMSet.ELEMENT)) {
rsmSet = new RSMSetProvider().parse(parser);
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getDepth() == initialDepth) {
break outerloop;
}
}
}
return new MamFinIQ(queryId, rsmSet, complete, stable);
}
}

View file

@ -0,0 +1,92 @@
/**
*
* 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.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.xmlpull.v1.XmlPullParser;
/**
* MAM Preferences IQ Provider class.
*
* @see <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @author Fernando Ramirez
*
*/
public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
@Override
public MamPrefsIQ parse(XmlPullParser parser, int initialDepth) throws Exception {
String iqType = parser.getAttributeValue("", "type");
String defaultField = parser.getAttributeValue("", "default");
if (iqType == null) {
iqType = "result";
}
List<Jid> alwaysJids = null;
List<Jid> neverJids = null;
outerloop: while (true) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("always")) {
alwaysJids = iterateJids(parser);
}
if (parser.getName().equals("never")) {
neverJids = iterateJids(parser);
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getDepth() == initialDepth) {
break outerloop;
}
}
}
return new MamPrefsIQ(Type.fromString(iqType), alwaysJids, neverJids, defaultField);
}
private List<Jid> iterateJids(XmlPullParser parser) throws Exception {
List<Jid> jids = new ArrayList<>();
int initialDepth = parser.getDepth();
outerloop: while (true) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("jid")) {
parser.next();
jids.add(JidCreate.from(parser.getText()));
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getDepth() == initialDepth) {
break outerloop;
}
}
}
return jids;
}
}

View file

@ -0,0 +1,58 @@
/**
*
* 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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @author Fernando Ramirez
*
*/
public class MamQueryIQProvider extends IQProvider<MamQueryIQ> {
@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) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals(DataForm.ELEMENT)) {
dataForm = new DataFormProvider().parse(parser);
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getDepth() == initialDepth) {
break outerloop;
}
}
}
return new MamQueryIQ(queryId, node, dataForm);
}
}

View file

@ -0,0 +1,57 @@
/**
*
* 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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
* @author Fernando Ramirez
*
*/
public class MamResultProvider extends ExtensionElementProvider<MamResultExtension> {
@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) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals(Forwarded.ELEMENT)) {
forwarded = new ForwardedProvider().parse(parser);
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getDepth() == initialDepth) {
break outerloop;
}
}
}
return new MamResultExtension(queryId, id, forwarded);
}
}

View file

@ -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 <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message
* Archive Management</a>
*
*/
package org.jivesoftware.smackx.mam.provider;

View file

@ -52,6 +52,28 @@
<className>org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider</className>
</extensionProvider>
<!-- XEP-0313 Message Archive Management -->
<iqProvider>
<elementName>prefs</elementName>
<namespace>urn:xmpp:mam:1</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider</className>
</iqProvider>
<iqProvider>
<elementName>query</elementName>
<namespace>urn:xmpp:mam:1</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamQueryIQProvider</className>
</iqProvider>
<iqProvider>
<elementName>fin</elementName>
<namespace>urn:xmpp:mam:1</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamFinIQProvider</className>
</iqProvider>
<extensionProvider>
<elementName>result</elementName>
<namespace>urn:xmpp:mam:1</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamResultProvider</className>
</extensionProvider>
<!-- XEP-0347: Internet of Things - Discovery -->
<iqProvider>
<elementName>register</elementName>