This is an automated email from the ASF dual-hosted git repository. ralaoui pushed a commit to branch xep-0313 in repository https://gitbox.apache.org/repos/asf/mina-vysper.git
commit 5df21b93e7f78cf42061cb192b648e19d49cf488 Author: Réda Housni Alaoui <reda.housniala...@gmail.com> AuthorDate: Tue Aug 6 13:54:52 2019 +0200 XEP-0313 Message Archive Management --- .../vysper/xmpp/datetime/DateTimeProfile.java | 6 ++ .../core/base/handler/AcceptedMessageEvent.java | 73 +++++++++++++++ .../modules/core/base/handler/MessageHandler.java | 31 +++++-- .../xmpp/stanza/dataforms/DataFormParser.java | 4 +- .../stanza/dataforms/DataFormParserTestCase.java | 4 +- server/extensions/pom.xml | 4 +- server/extensions/xep0313-mam/pom.xml | 64 +++++++++++++ .../xep0313_mam/MAMEventListenerDictionary.java | 38 ++++++++ .../modules/extension/xep0313_mam/MAMModule.java | 103 +++++++++++++++++++++ .../extension/xep0313_mam/MAMNamespaceURIs.java | 33 +++++++ .../xep0313_mam/archiving/SimpleMessage.java | 57 ++++++++++++ .../archiving/user/UserMessageListener.java | 82 ++++++++++++++++ .../extension/xep0313_mam/query/MAMIQHandler.java | 77 +++++++++++++++ .../xep0313_mam/query/MessageUnpagedRequest.java | 46 +++++++++ .../modules/extension/xep0313_mam/query/Query.java | 68 ++++++++++++++ .../modules/extension/xep0313_mam/query/X.java | 77 +++++++++++++++ .../extension/xep0313_mam/spi/ArchivedMessage.java | 34 +++++++ .../xep0313_mam/spi/ArchivedMessages.java | 47 ++++++++++ .../extension/xep0313_mam/spi/DateTimeFilter.java | 48 ++++++++++ .../modules/extension/xep0313_mam/spi/Message.java | 35 +++++++ .../xep0313_mam/spi/MessagePageRequest.java | 36 +++++++ .../xep0313_mam/spi/SimpleArchivedMessage.java | 60 ++++++++++++ .../xep0313_mam/spi/SimpleArchivedMessages.java | 67 ++++++++++++++ .../xep0313_mam/spi/user/UserMessageArchive.java | 46 +++++++++ .../xep0313_mam/spi/user/UserMessageArchives.java | 34 +++++++ .../extension/xep0313_mam/MAMModuleTest.java | 77 +++++++++++++++ 26 files changed, 1236 insertions(+), 15 deletions(-) diff --git a/server/core/src/main/java/org/apache/vysper/xmpp/datetime/DateTimeProfile.java b/server/core/src/main/java/org/apache/vysper/xmpp/datetime/DateTimeProfile.java index 21377b4..65ee034 100644 --- a/server/core/src/main/java/org/apache/vysper/xmpp/datetime/DateTimeProfile.java +++ b/server/core/src/main/java/org/apache/vysper/xmpp/datetime/DateTimeProfile.java @@ -22,6 +22,7 @@ package org.apache.vysper.xmpp.datetime; import static org.apache.vysper.compliance.SpecCompliant.ComplianceCoverage.COMPLETE; import static org.apache.vysper.compliance.SpecCompliant.ComplianceStatus.IN_PROGRESS; +import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; @@ -122,6 +123,11 @@ public class DateTimeProfile { throw new IllegalArgumentException("Invalid date time: " + time); } } + + public ZonedDateTime fromZonedDateTime(String time){ + Calendar calendar = fromDateTime(time); + return ZonedDateTime.ofInstant(calendar.toInstant(), calendar.getTimeZone().toZoneId()); + } /** * Parses a time, compliant with ISO-8601 and XEP-0082. diff --git a/server/core/src/main/java/org/apache/vysper/xmpp/modules/core/base/handler/AcceptedMessageEvent.java b/server/core/src/main/java/org/apache/vysper/xmpp/modules/core/base/handler/AcceptedMessageEvent.java new file mode 100644 index 0000000..8ec663a --- /dev/null +++ b/server/core/src/main/java/org/apache/vysper/xmpp/modules/core/base/handler/AcceptedMessageEvent.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.core.base.handler; + +import static java.util.Objects.requireNonNull; +import static java.util.Optional.ofNullable; + +import java.util.Optional; + +import org.apache.vysper.xmpp.server.ServerRuntimeContext; +import org.apache.vysper.xmpp.server.SessionContext; +import org.apache.vysper.xmpp.stanza.MessageStanza; + +/** + * @author Réda Housni Alaoui + */ +public class AcceptedMessageEvent { + + private final ServerRuntimeContext serverRuntimeContext; + + private final SessionContext sessionContext; + + private final boolean outbound; + + private final MessageStanza messageStanza; + + public AcceptedMessageEvent(ServerRuntimeContext serverRuntimeContext, SessionContext sessionContext, + boolean outbound, MessageStanza messageStanza) { + this.serverRuntimeContext = requireNonNull(serverRuntimeContext); + this.sessionContext = sessionContext; + this.outbound = outbound; + this.messageStanza = requireNonNull(messageStanza); + } + + public ServerRuntimeContext serverRuntimeContext() { + return serverRuntimeContext; + } + + public Optional<SessionContext> sessionContext() { + return ofNullable(sessionContext); + } + + public boolean isOutbound() { + return outbound; + } + + public MessageStanza messageStanza() { + return messageStanza; + } + + @Override + public String toString() { + return "AcceptedMessageEvent{" + "serverRuntimeContext=" + serverRuntimeContext + ", sessionContext=" + + sessionContext + ", outbound=" + outbound + ", messageStanza=" + messageStanza + '}'; + } +} diff --git a/server/core/src/main/java/org/apache/vysper/xmpp/modules/core/base/handler/MessageHandler.java b/server/core/src/main/java/org/apache/vysper/xmpp/modules/core/base/handler/MessageHandler.java index f5ed4c8..3d4a302 100644 --- a/server/core/src/main/java/org/apache/vysper/xmpp/modules/core/base/handler/MessageHandler.java +++ b/server/core/src/main/java/org/apache/vysper/xmpp/modules/core/base/handler/MessageHandler.java @@ -20,6 +20,7 @@ package org.apache.vysper.xmpp.modules.core.base.handler; +import org.apache.vysper.event.EventBus; import org.apache.vysper.xml.fragment.Attribute; import org.apache.vysper.xml.fragment.XMLElement; import org.apache.vysper.xml.fragment.XMLSemanticError; @@ -40,6 +41,7 @@ import org.apache.vysper.xmpp.stanza.XMPPCoreStanza; * @author The Apache MINA Project (d...@mina.apache.org) */ public class MessageHandler extends XMPPCoreStanzaHandler { + public String getName() { return "message"; } @@ -87,13 +89,8 @@ public class MessageHandler extends XMPPCoreStanzaHandler { // TODO inspect all BODY elements and make sure they conform to the spec + EventBus eventBus = serverRuntimeContext.getEventBus(); if (isOutboundStanza) { - // check if message reception is turned of either globally or locally - if (!serverRuntimeContext.getServerFeatures().isRelayingMessages() - || (sessionContext != null && sessionContext - .getAttribute(SessionContext.SESSION_ATTRIBUTE_MESSAGE_STANZA_NO_RECEIVE) != null)) { - return null; - } Entity from = stanza.getFrom(); if (from == null || !from.isResourceSet()) { @@ -116,17 +113,31 @@ public class MessageHandler extends XMPPCoreStanzaHandler { stanza = XMPPCoreStanza.getWrapper(stanzaBuilder.build()); } + eventBus.publish(AcceptedMessageEvent.class, + new AcceptedMessageEvent(serverRuntimeContext, sessionContext, true, new MessageStanza(stanza))); + + // check if message reception is turned of either globally or locally + if (!serverRuntimeContext.getServerFeatures().isRelayingMessages() + || (sessionContext != null && sessionContext + .getAttribute(SessionContext.SESSION_ATTRIBUTE_MESSAGE_STANZA_NO_RECEIVE) != null)) { + return null; + } + StanzaRelay stanzaRelay = serverRuntimeContext.getStanzaRelay(); try { stanzaRelay.relay(stanza.getTo(), stanza, new ReturnErrorToSenderFailureStrategy(stanzaRelay)); } catch (Exception e) { // TODO return error stanza - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + e.printStackTrace(); // To change body of catch statement use File | Settings | File Templates. } - } else if (sessionContext != null) { - sessionContext.getResponseWriter().write(stanza); } else { - throw new IllegalStateException("handling offline messages not implemented"); + eventBus.publish(AcceptedMessageEvent.class, + new AcceptedMessageEvent(serverRuntimeContext, sessionContext, false, new MessageStanza(stanza))); + if (sessionContext != null) { + sessionContext.getResponseWriter().write(stanza); + } else { + throw new IllegalStateException("handling offline messages not implemented"); + } } return null; } diff --git a/server/core/src/main/java/org/apache/vysper/xmpp/stanza/dataforms/DataFormParser.java b/server/core/src/main/java/org/apache/vysper/xmpp/stanza/dataforms/DataFormParser.java index 0682bca..6efd971 100644 --- a/server/core/src/main/java/org/apache/vysper/xmpp/stanza/dataforms/DataFormParser.java +++ b/server/core/src/main/java/org/apache/vysper/xmpp/stanza/dataforms/DataFormParser.java @@ -76,7 +76,7 @@ public class DataFormParser { public Map<String, Object> extractFieldValues() throws IllegalArgumentException { Map<String,Object> map = new LinkedHashMap<String, Object>(); - for (XMLElement fields : form.getInnerElementsNamed("field", NamespaceURIs.JABBER_X_DATA)) { + for (XMLElement fields : form.getInnerElementsNamed("field")) { final String varName = fields.getAttributeValue("var"); final String typeName = fields.getAttributeValue("type"); String valueAsString = null; @@ -93,7 +93,7 @@ public class DataFormParser { boolean isMulti = Field.Type.isMulti(fieldType); List<Object> values = isMulti ? new ArrayList<Object>() : null; - for (XMLElement valueCandidates : fields.getInnerElementsNamed("value", NamespaceURIs.JABBER_X_DATA)) { + for (XMLElement valueCandidates : fields.getInnerElementsNamed("value")) { final XMLText firstInnerText = valueCandidates.getFirstInnerText(); if (firstInnerText != null) valueAsString = firstInnerText.getText(); Object value; diff --git a/server/core/src/test/java/org/apache/vysper/xmpp/stanza/dataforms/DataFormParserTestCase.java b/server/core/src/test/java/org/apache/vysper/xmpp/stanza/dataforms/DataFormParserTestCase.java index 650ce83..a3c92a0 100644 --- a/server/core/src/test/java/org/apache/vysper/xmpp/stanza/dataforms/DataFormParserTestCase.java +++ b/server/core/src/test/java/org/apache/vysper/xmpp/stanza/dataforms/DataFormParserTestCase.java @@ -134,14 +134,14 @@ public class DataFormParserTestCase { private void assertMultiValue(String type, List<String> values, List<Object> expectedValues) { XMLElementBuilder builder = new XMLElementBuilder("x", NamespaceURIs.JABBER_X_DATA) - .startInnerElement("field", NamespaceURIs.JABBER_X_DATA) + .startInnerElement("field") .addAttribute("var", "fie1"); if(type != null) builder.addAttribute("type", type); for(String value : values) { - builder.startInnerElement("value", NamespaceURIs.JABBER_X_DATA); + builder.startInnerElement("value"); builder.addText(value); builder.endInnerElement(); } diff --git a/server/extensions/pom.xml b/server/extensions/pom.xml index ecc6513..9c82aeb 100644 --- a/server/extensions/pom.xml +++ b/server/extensions/pom.xml @@ -35,6 +35,7 @@ <module>xep0045-muc</module> <module>xep0065-socks</module> <module>xep0124-xep0206-bosh</module> + <module>xep0313-mam</module> <module>websockets</module> </modules> @@ -45,8 +46,9 @@ <module>xep0060-pubsub</module> <module>xep0045-muc</module> <module>xep0045-muc-inttest</module> - <module>xep0124-xep0206-bosh</module> <module>xep0065-socks</module> + <module>xep0124-xep0206-bosh</module> + <module>xep0313-mam</module> <module>websockets</module> </modules> </profile> diff --git a/server/extensions/xep0313-mam/pom.xml b/server/extensions/xep0313-mam/pom.xml new file mode 100644 index 0000000..be68839 --- /dev/null +++ b/server/extensions/xep0313-mam/pom.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to you 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. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <parent> + <artifactId>vysper-extensions</artifactId> + <groupId>org.apache.vysper</groupId> + <version>0.8-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.apache.vysper.extensions</groupId> + <artifactId>xep0313-mam</artifactId> + <name>Apache Vysper XEP-0313 Message Archive Management</name> + <version>0.8-SNAPSHOT</version> + + + <dependencies> + <dependency> + <groupId>org.apache.vysper</groupId> + <artifactId>spec-compliance</artifactId> + <optional>true</optional> + </dependency> + + <dependency> + <groupId>org.apache.vysper</groupId> + <artifactId>vysper-core</artifactId> + </dependency> + + <!-- Runtime dependencies --> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>runtime</scope> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMEventListenerDictionary.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMEventListenerDictionary.java new file mode 100644 index 0000000..de4126e --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMEventListenerDictionary.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam; + +import org.apache.vysper.event.EventListener; +import org.apache.vysper.event.EventListenerDictionary; + +import java.util.Set; + +/** + * @author Réda Housni Alaoui + */ +public class MAMEventListenerDictionary implements EventListenerDictionary { + + + + @Override + public Set<EventListener<?>> get(Class<?> eventType) { + return null; + } +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModule.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModule.java new file mode 100644 index 0000000..59d9770 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModule.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.lang.StringUtils; +import org.apache.vysper.event.EventListenerDictionary; +import org.apache.vysper.event.SimpleEventListenerDictionary; +import org.apache.vysper.xmpp.modules.DefaultDiscoAwareModule; +import org.apache.vysper.xmpp.modules.core.base.handler.AcceptedMessageEvent; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.archiving.user.UserMessageListener; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.MAMIQHandler; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.user.UserMessageArchives; +import org.apache.vysper.xmpp.modules.servicediscovery.management.Feature; +import org.apache.vysper.xmpp.modules.servicediscovery.management.InfoElement; +import org.apache.vysper.xmpp.modules.servicediscovery.management.InfoRequest; +import org.apache.vysper.xmpp.modules.servicediscovery.management.ServerInfoRequestListener; +import org.apache.vysper.xmpp.protocol.HandlerDictionary; +import org.apache.vysper.xmpp.protocol.NamespaceHandlerDictionary; +import org.apache.vysper.xmpp.protocol.NamespaceURIs; +import org.apache.vysper.xmpp.server.ServerRuntimeContext; + +/** + * A module for <a href="https://xmpp.org/extensions/xep-0313.html">XEP-0313 + * Message Archive Management</a> + * + * @author Réda Housni Alaoui + */ +public class MAMModule extends DefaultDiscoAwareModule implements ServerInfoRequestListener { + + private final UserMessageListener userMessageListener = new UserMessageListener(); + + @Override + public void initialize(ServerRuntimeContext serverRuntimeContext) { + super.initialize(serverRuntimeContext); + + requireNonNull((UserMessageArchives) serverRuntimeContext.getStorageProvider(UserMessageArchives.class), + "Could not find an instance of " + UserMessageArchives.class); + } + + @Override + public String getName() { + return "XEP-0313 Message Archive Management"; + } + + @Override + public String getVersion() { + return "0.6.3"; + } + + @Override + protected void addServerInfoRequestListeners(List<ServerInfoRequestListener> serverInfoRequestListeners) { + serverInfoRequestListeners.add(this); + } + + @Override + public List<InfoElement> getServerInfosFor(InfoRequest request) { + if (StringUtils.isNotEmpty(request.getNode())) { + return null; + } + + List<InfoElement> infoElements = new ArrayList<>(); + infoElements.add(new Feature(MAMNamespaceURIs.CORE)); + infoElements.add(new Feature(MAMNamespaceURIs.XEP0359_STANZA_IDS)); + infoElements.add(new Feature(NamespaceURIs.JABBER_X_DATA)); + return infoElements; + } + + @Override + protected void addHandlerDictionaries(List<HandlerDictionary> dictionary) { + MAMIQHandler iqHandler = new MAMIQHandler(); + dictionary.add(new NamespaceHandlerDictionary(MAMNamespaceURIs.CORE, iqHandler)); + } + + @Override + public Optional<EventListenerDictionary> getEventListenerDictionary() { + EventListenerDictionary dictionary = SimpleEventListenerDictionary.builder() + .register(AcceptedMessageEvent.class, userMessageListener).build(); + return Optional.of(dictionary); + } +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMNamespaceURIs.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMNamespaceURIs.java new file mode 100644 index 0000000..13a17a7 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMNamespaceURIs.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam; + +/** + * @author Réda Housni Alaoui + */ +public class MAMNamespaceURIs { + + public static final String CORE = "urn:xmpp:mam:2"; + public static final String XEP0359_STANZA_IDS = "urn:xmpp:sid:0"; + + private MAMNamespaceURIs() { + } + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/archiving/SimpleMessage.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/archiving/SimpleMessage.java new file mode 100644 index 0000000..07f9e60 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/archiving/SimpleMessage.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.archiving; + +import static java.util.Objects.requireNonNull; + +import java.time.ZonedDateTime; + +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.Message; +import org.apache.vysper.xmpp.stanza.MessageStanza; + +/** + * @author Réda Housni Alaoui + */ +public class SimpleMessage implements Message { + + private final MessageStanza stanza; + + private final ZonedDateTime dateTime; + + public SimpleMessage(MessageStanza stanza) { + this.stanza = requireNonNull(stanza); + this.dateTime = ZonedDateTime.now(); + } + + @Override + public MessageStanza stanza() { + return stanza; + } + + @Override + public ZonedDateTime dateTime() { + return dateTime; + } + + @Override + public String toString() { + return "SimpleMessage{" + "stanza=" + stanza + ", dateTime=" + dateTime + '}'; + } +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/archiving/user/UserMessageListener.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/archiving/user/UserMessageListener.java new file mode 100644 index 0000000..a815e95 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/archiving/user/UserMessageListener.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.archiving.user; + +import static java.util.Objects.requireNonNull; + +import java.util.Optional; + +import org.apache.vysper.event.EventListener; +import org.apache.vysper.xmpp.addressing.Entity; +import org.apache.vysper.xmpp.modules.core.base.handler.AcceptedMessageEvent; +import org.apache.vysper.xmpp.modules.core.base.handler.XMPPCoreStanzaHandler; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.archiving.SimpleMessage; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.user.UserMessageArchive; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.user.UserMessageArchives; +import org.apache.vysper.xmpp.server.ServerRuntimeContext; +import org.apache.vysper.xmpp.server.SessionContext; +import org.apache.vysper.xmpp.stanza.MessageStanza; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Réda Housni Alaoui + */ +public class UserMessageListener implements EventListener<AcceptedMessageEvent> { + + private static final Logger LOG = LoggerFactory.getLogger(UserMessageListener.class); + + @Override + public void onEvent(AcceptedMessageEvent acceptedMessage) { + archive(acceptedMessage); + } + + private void archive(AcceptedMessageEvent acceptedMessage) { + ServerRuntimeContext serverRuntimeContext = acceptedMessage.serverRuntimeContext(); + UserMessageArchives archives = requireNonNull( + (UserMessageArchives) serverRuntimeContext.getStorageProvider(UserMessageArchives.class), + "Could not find an instance of " + UserMessageArchives.class); + + MessageStanza messageStanza = acceptedMessage.messageStanza(); + + Entity archiveJID; + if (acceptedMessage.isOutbound()) { + // We will store the message in the sender archive + SessionContext sessionContext = acceptedMessage.sessionContext() + .orElseThrow(() -> new IllegalStateException("No session context found in " + acceptedMessage)); + archiveJID = XMPPCoreStanzaHandler.extractSenderJID(messageStanza, sessionContext); + } else { + // We will store the message in the receiver archive + archiveJID = requireNonNull(messageStanza.getTo(), "No 'to' found in " + messageStanza); + } + + // Servers that expose archive messages of sent/received messages on behalf of + // local users MUST expose these archives to the user on the user's bare JID. + archiveJID = archiveJID.getBareJID(); + + Optional<UserMessageArchive> userArchive = archives.retrieve(archiveJID); + if (!userArchive.isPresent()) { + LOG.debug("No archive returned for user with bare JID '{}'", archiveJID); + return; + } + + userArchive.get().archive(new SimpleMessage(messageStanza)); + } +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQHandler.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQHandler.java new file mode 100644 index 0000000..a1dede2 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQHandler.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.query; + +import static java.util.Optional.ofNullable; + +import org.apache.vysper.xml.fragment.XMLSemanticError; +import org.apache.vysper.xmpp.addressing.Entity; +import org.apache.vysper.xmpp.modules.core.base.handler.DefaultIQHandler; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.MAMNamespaceURIs; +import org.apache.vysper.xmpp.server.ServerRuntimeContext; +import org.apache.vysper.xmpp.server.SessionContext; +import org.apache.vysper.xmpp.server.response.ServerErrorResponses; +import org.apache.vysper.xmpp.stanza.IQStanza; +import org.apache.vysper.xmpp.stanza.Stanza; +import org.apache.vysper.xmpp.stanza.StanzaErrorCondition; +import org.apache.vysper.xmpp.stanza.StanzaErrorType; + +/** + * @author Réda Housni Alaoui + */ +public class MAMIQHandler extends DefaultIQHandler { + + private static final String QUERY = "query"; + + @Override + protected boolean verifyInnerElement(Stanza stanza) { + return verifyInnerElementWorker(stanza, QUERY) && verifyInnerNamespace(stanza, MAMNamespaceURIs.CORE); + } + + @Override + protected Stanza handleSet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext, + SessionContext sessionContext) { + Query query; + try { + query = Query.parse(stanza); + } catch (XMLSemanticError xmlSemanticError) { + return ServerErrorResponses.getStanzaError(StanzaErrorCondition.NOT_ACCEPTABLE, stanza, + StanzaErrorType.CANCEL, null, null, null); + } + + if (query.getNode().isPresent()) { + // PubSub node archives is not yet implemented + return ServerErrorResponses.getStanzaError(StanzaErrorCondition.FEATURE_NOT_IMPLEMENTED, stanza, + StanzaErrorType.CANCEL, null, null, null); + } + + Entity queriedEntity = ofNullable(stanza.getTo()).orElse(sessionContext.getInitiatingEntity()); + + if (!serverRuntimeContext.getServerEnitity().getDomain().equals(queriedEntity.getDomain())) { + // MUC archives is not yet implemented + return ServerErrorResponses.getStanzaError(StanzaErrorCondition.FEATURE_NOT_IMPLEMENTED, stanza, + StanzaErrorType.MODIFY, null, null, null); + } + + + + return null; + } +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MessageUnpagedRequest.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MessageUnpagedRequest.java new file mode 100644 index 0000000..622357c --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MessageUnpagedRequest.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.query; + +import java.util.Optional; +import java.util.OptionalLong; + +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessagePageRequest; + +/** + * @author Réda Housni Alaoui + */ +public class MessageUnpagedRequest implements MessagePageRequest { + + @Override + public OptionalLong pageSize() { + return OptionalLong.empty(); + } + + @Override + public Optional<String> firstMessageId() { + return Optional.empty(); + } + + @Override + public Optional<String> lastMessageId() { + return Optional.empty(); + } +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/Query.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/Query.java new file mode 100644 index 0000000..eceae36 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/Query.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.query; + +import static java.util.Optional.ofNullable; + +import java.util.Optional; + +import org.apache.vysper.xml.fragment.XMLElement; +import org.apache.vysper.xml.fragment.XMLSemanticError; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.MAMNamespaceURIs; +import org.apache.vysper.xmpp.stanza.IQStanza; + +/** + * @author Réda Housni Alaoui + */ +public class Query { + + private static final String ELEMENT_NAME = "query"; + + private final XMLElement element; + + public Query(XMLElement element) { + if (!ELEMENT_NAME.equals(element.getName())) { + throw new IllegalArgumentException( + "Query element must be named '" + ELEMENT_NAME + "' instead of '" + element.getName() + "'"); + } + if (!MAMNamespaceURIs.CORE.equals(element.getNamespaceURI())) { + throw new IllegalArgumentException("Query element must be bound to namespace uri '" + MAMNamespaceURIs.CORE + + "' instead of '" + element.getNamespaceURI() + "'"); + } + this.element = element; + } + + public static Query parse(IQStanza iqStanza) throws XMLSemanticError { + return new Query(iqStanza.getSingleInnerElementsNamed(ELEMENT_NAME)); + } + + public Optional<String> getQueryId() { + return ofNullable(element.getAttributeValue("queryid")); + } + + public Optional<String> getNode() { + return ofNullable(element.getAttributeValue("node")); + } + + public Optional<X> getX() throws XMLSemanticError { + return ofNullable(element.getSingleInnerElementsNamed(X.ELEMENT_NAME)).map(X::new); + } + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/X.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/X.java new file mode 100644 index 0000000..f0174ca --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/X.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.query; + +import static java.util.Optional.ofNullable; + +import java.time.ZonedDateTime; +import java.util.Map; +import java.util.Optional; + +import org.apache.vysper.xml.fragment.XMLElement; +import org.apache.vysper.xmpp.addressing.Entity; +import org.apache.vysper.xmpp.addressing.EntityImpl; +import org.apache.vysper.xmpp.datetime.DateTimeProfile; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.MAMNamespaceURIs; +import org.apache.vysper.xmpp.protocol.NamespaceURIs; +import org.apache.vysper.xmpp.stanza.dataforms.DataFormParser; + +/** + * @author Réda Housni Alaoui + */ +public class X { + + public static final String ELEMENT_NAME = "x"; + + private final Map<String, Object> fields; + + public X(XMLElement element) { + if (!ELEMENT_NAME.equals(element.getName())) { + throw new IllegalArgumentException( + "Query element must be named '" + ELEMENT_NAME + "' instead of '" + element.getName() + "'"); + } + if (!NamespaceURIs.JABBER_X_DATA.equals(element.getNamespaceURI())) { + throw new IllegalArgumentException("Query element must be bound to namespace uri '" + MAMNamespaceURIs.CORE + + "' instead of '" + element.getNamespaceURI() + "'"); + } + fields = new DataFormParser(element).extractFieldValues(); + } + + private Optional<String> getStringValue(String fieldName) { + return ofNullable(fields.get(fieldName)).map(String.class::cast); + } + + private Optional<ZonedDateTime> getZonedDateTime(String fieldName) { + return getStringValue(fieldName).map(stringDate -> DateTimeProfile.getInstance().fromZonedDateTime(stringDate)); + } + + public Optional<Entity> getWith() { + return getStringValue("with").map(EntityImpl::parseUnchecked); + } + + public Optional<ZonedDateTime> getStart() { + return getZonedDateTime("start"); + } + + public Optional<ZonedDateTime> getEnd() { + return getZonedDateTime("end"); + } + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/ArchivedMessage.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/ArchivedMessage.java new file mode 100644 index 0000000..3a20f30 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/ArchivedMessage.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.spi; + +/** + * @author Réda Housni Alaoui + */ +public interface ArchivedMessage extends Message { + + /** + * @return The id of the message in its archive as defined in <a + * href="https://xmpp.org/extensions/xep-0313.html#archives_id">Communicating + * the archive ID</a> + */ + String id(); + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/ArchivedMessages.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/ArchivedMessages.java new file mode 100644 index 0000000..4cb6c54 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/ArchivedMessages.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.spi; + +import java.util.List; +import java.util.OptionalLong; + +/** + * @author Réda Housni Alaoui + */ +public interface ArchivedMessages { + + List<ArchivedMessage> list(); + + /** + * This integer specifies the position within the full set (which MAY be + * approximate) of the first message in the page. If that message is the first + * in the full set, then the index SHOULD be '0'. If the last message in the + * page is the last message in the full set, then the value SHOULD be the + * specified count minus the number of messages in the last page. + */ + OptionalLong firstMessageIndex(); + + /** + * @return The total number of messages that could be retrieved by paging + * through the pages. + */ + OptionalLong totalNumberOfMessages(); + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/DateTimeFilter.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/DateTimeFilter.java new file mode 100644 index 0000000..0a033d8 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/DateTimeFilter.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.spi; + +import java.time.ZonedDateTime; +import java.util.Optional; + +/** + * <a href="https://xmpp.org/extensions/xep-0313.html#filter-time">Filtering by + * time received</a> + * + * @author Réda Housni Alaoui + */ +public interface DateTimeFilter { + + /** + * The 'start' field is used to filter out messages before a certain date/time. + * If specified, a server MUST only return messages whose timestamp is equal to + * or later than the given timestamp. + */ + Optional<ZonedDateTime> start(); + + /** + * The 'end' field is used to exclude from the results messages + * after a certain point in time. If specified, a server MUST only return + * messages whose timestamp is equal to or earlier than the timestamp given in + * the 'end' field. + */ + Optional<ZonedDateTime> end(); + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/Message.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/Message.java new file mode 100644 index 0000000..0ef98e5 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/Message.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.spi; + +import java.time.ZonedDateTime; + +import org.apache.vysper.xmpp.stanza.MessageStanza; + +/** + * @author Réda Housni Alaoui + */ +public interface Message { + + MessageStanza stanza(); + + ZonedDateTime dateTime(); + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessagePageRequest.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessagePageRequest.java new file mode 100644 index 0000000..37a9b46 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessagePageRequest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.spi; + +import java.util.Optional; +import java.util.OptionalLong; + +/** + * @author Réda Housni Alaoui + */ +public interface MessagePageRequest { + + OptionalLong pageSize(); + + Optional<String> firstMessageId(); + + Optional<String> lastMessageId(); + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/SimpleArchivedMessage.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/SimpleArchivedMessage.java new file mode 100644 index 0000000..c884b98 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/SimpleArchivedMessage.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.spi; + +import static java.util.Objects.requireNonNull; + +import java.time.ZonedDateTime; + +import org.apache.vysper.xmpp.stanza.MessageStanza; + +/** + * @author Réda Housni Alaoui + */ +public class SimpleArchivedMessage implements ArchivedMessage { + + private final String id; + + private final ZonedDateTime dateTime; + + private final MessageStanza stanza; + + public SimpleArchivedMessage(String id, ZonedDateTime dateTime, MessageStanza stanza) { + this.id = requireNonNull(id); + this.dateTime = requireNonNull(dateTime); + this.stanza = requireNonNull(stanza); + } + + @Override + public MessageStanza stanza() { + return stanza; + } + + @Override + public ZonedDateTime dateTime() { + return dateTime; + } + + @Override + public String id() { + return id; + } + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/SimpleArchivedMessages.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/SimpleArchivedMessages.java new file mode 100644 index 0000000..2606e41 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/SimpleArchivedMessages.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.spi; + +import static java.util.Optional.ofNullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.OptionalLong; + +/** + * @author Réda Housni Alaoui + */ +public class SimpleArchivedMessages implements ArchivedMessages { + + private final List<ArchivedMessage> list; + + private final Long firstMessageIndex; + + private final Long totalNumberOfMessages; + + public SimpleArchivedMessages(List<ArchivedMessage> list) { + this(list, null, null); + } + + public SimpleArchivedMessages(List<ArchivedMessage> list, Long firstMessageIndex) { + this(list, firstMessageIndex, null); + } + + public SimpleArchivedMessages(List<ArchivedMessage> list, Long firstMessageIndex, Long totalNumberOfMessages) { + this.list = new ArrayList<>(list); + this.firstMessageIndex = firstMessageIndex; + this.totalNumberOfMessages = totalNumberOfMessages; + } + + @Override + public List<ArchivedMessage> list() { + return new ArrayList<>(list); + } + + @Override + public OptionalLong firstMessageIndex() { + return ofNullable(firstMessageIndex).map(OptionalLong::of).orElse(OptionalLong.empty()); + } + + @Override + public OptionalLong totalNumberOfMessages() { + return ofNullable(totalNumberOfMessages).map(OptionalLong::of).orElse(OptionalLong.empty()); + } +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/user/UserMessageArchive.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/user/UserMessageArchive.java new file mode 100644 index 0000000..82e3a5d --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/user/UserMessageArchive.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.user; + +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.ArchivedMessages; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.DateTimeFilter; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.Message; +import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessagePageRequest; + +/** + * <a href= + * "https://xmpp.org/extensions/xep-0313.html#business-storeret-user-archives">User + * Archives</a> + * + * @author Réda Housni Alaoui + */ +public interface UserMessageArchive { + + /** + * At a minimum, the server MUST store the <body> elements of a stanza. It is + * suggested that other elements that are used in a given deployment to + * supplement conversations (e.g. XHTML-IM payloads) are also stored. Other + * elements MAY be stored. + */ + void archive(Message message); + + ArchivedMessages fetch(DateTimeFilter dateTimeRange, MessagePageRequest pageRequest); + +} diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/user/UserMessageArchives.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/user/UserMessageArchives.java new file mode 100644 index 0000000..8620c75 --- /dev/null +++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/user/UserMessageArchives.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.user; + +import java.util.Optional; + +import org.apache.vysper.storage.StorageProvider; +import org.apache.vysper.xmpp.addressing.Entity; + +/** + * @author Réda Housni Alaoui + */ +public interface UserMessageArchives extends StorageProvider { + + Optional<UserMessageArchive> retrieve(Entity userBareJid); + +} diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModuleTest.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModuleTest.java new file mode 100644 index 0000000..e5d493b --- /dev/null +++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModuleTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.vysper.xmpp.modules.servicediscovery.management.Feature; +import org.apache.vysper.xmpp.modules.servicediscovery.management.InfoElement; +import org.apache.vysper.xmpp.modules.servicediscovery.management.InfoRequest; +import org.apache.vysper.xmpp.modules.servicediscovery.management.ServerInfoRequestListener; +import org.apache.vysper.xmpp.modules.servicediscovery.management.ServiceDiscoveryRequestException; +import org.junit.Test; + +import junit.framework.Assert; + +/** + * @author The Apache MINA Project (d...@mina.apache.org) + */ +public class MAMModuleTest { + + private MAMModule tested = new MAMModule(); + + @Test + public void nameMustBeProvided() { + Assert.assertNotNull(tested.getName()); + } + + @Test + public void versionMustBeProvided() { + Assert.assertNotNull(tested.getVersion()); + } + + @Test + public void getServerInfosFor() throws ServiceDiscoveryRequestException { + List<ServerInfoRequestListener> serverInfoRequestListeners = new ArrayList<>(); + + tested.addServerInfoRequestListeners(serverInfoRequestListeners); + + Assert.assertEquals(1, serverInfoRequestListeners.size()); + + List<InfoElement> infoElements = serverInfoRequestListeners.get(0) + .getServerInfosFor(new InfoRequest(null, null, null, null)); + + Assert.assertEquals(1, infoElements.size()); + Assert.assertTrue(infoElements.get(0) instanceof Feature); + Assert.assertEquals(MAMNamespaceURIs.CORE, ((Feature) infoElements.get(0)).getVar()); + } + + @Test + public void getServerInfosForWithNode() throws ServiceDiscoveryRequestException { + List<ServerInfoRequestListener> serverInfoRequestListeners = new ArrayList<>(); + tested.addServerInfoRequestListeners(serverInfoRequestListeners); + Assert.assertEquals(1, serverInfoRequestListeners.size()); + + Assert.assertNull( + serverInfoRequestListeners.get(0).getServerInfosFor(new InfoRequest(null, null, "node", null))); + } + +} \ No newline at end of file