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 6b86cb482e3db29567208b278fcef22427ee882a
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    |  75 +++++++++++++++
 .../modules/core/base/handler/MessageHandler.java  |  27 ++++--
 .../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        |  81 ++++++++++++++++
 .../extension/xep0313_mam/query/MAMIQHandler.java  |  78 ++++++++++++++++
 .../xep0313_mam/query/MAMQueryHandler.java         |  38 ++++++++
 .../xep0313_mam/query/MessageUnpagedRequest.java   |  46 +++++++++
 .../modules/extension/xep0313_mam/query/Query.java |  71 ++++++++++++++
 .../modules/extension/xep0313_mam/query/X.java     |  77 +++++++++++++++
 .../query/muc/MUCArchiveQueryHandler.java          |  55 +++++++++++
 .../pubsub/PubsubNodeArchiveQueryHandler.java      |  51 ++++++++++
 .../xep0313_mam/query/user/UserArchiveQuery.java   |  52 +++++++++++
 .../query/user/UserArchiveQueryHandler.java        |  87 +++++++++++++++++
 .../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 +++++++++++++++
 31 files changed, 1520 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..a81426c
--- /dev/null
+++ 
b/server/core/src/main/java/org/apache/vysper/xmpp/modules/core/base/handler/AcceptedMessageEvent.java
@@ -0,0 +1,75 @@
+/*
+ *  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 = requireNonNull(sessionContext);
+        this.outbound = outbound;
+        this.messageStanza = requireNonNull(messageStanza);
+    }
+
+    public ServerRuntimeContext serverRuntimeContext() {
+        return serverRuntimeContext;
+    }
+
+    public SessionContext sessionContext() {
+        return 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..1ae49c6 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,27 @@ 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)));
+            sessionContext.getResponseWriter().write(stanza);
         }
         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..133d353
--- /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,81 @@
+/*
+ *  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();
+            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..1d1a6d7
--- /dev/null
+++ 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQHandler.java
@@ -0,0 +1,78 @@
+/*
+ *  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.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.vysper.xml.fragment.XMLSemanticError;
+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.modules.extension.xep0313_mam.query.muc.MUCArchiveQueryHandler;
+import 
org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.pubsub.PubsubNodeArchiveQueryHandler;
+import 
org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.user.UserArchiveQueryHandler;
+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 final List<MAMQueryHandler> queryHandlers;
+
+    public MAMIQHandler() {
+        List<MAMQueryHandler> queryHandlers = new ArrayList<>();
+        queryHandlers.add(new PubsubNodeArchiveQueryHandler());
+        queryHandlers.add(new MUCArchiveQueryHandler());
+        queryHandlers.add(new UserArchiveQueryHandler());
+        this.queryHandlers = Collections.unmodifiableList(queryHandlers);
+    }
+
+    @Override
+    protected boolean verifyInnerElement(Stanza stanza) {
+        return verifyInnerElementWorker(stanza, Query.ELEMENT_NAME)
+                && verifyInnerNamespace(stanza, MAMNamespaceURIs.CORE);
+    }
+
+    @Override
+    protected Stanza handleSet(IQStanza stanza, ServerRuntimeContext 
serverRuntimeContext,
+            SessionContext sessionContext) {
+        Query query;
+        try {
+            query = new Query(stanza);
+        } catch (XMLSemanticError xmlSemanticError) {
+            return 
ServerErrorResponses.getStanzaError(StanzaErrorCondition.NOT_ACCEPTABLE, stanza,
+                    StanzaErrorType.CANCEL, null, null, null);
+        }
+
+        return queryHandlers.stream().map(handler -> handler.handle(query, 
serverRuntimeContext, sessionContext))
+                .filter(Optional::isPresent).map(Optional::get).findFirst()
+                .orElseGet(() -> 
ServerErrorResponses.getStanzaError(StanzaErrorCondition.NOT_ACCEPTABLE, stanza,
+                        StanzaErrorType.CANCEL, null, null, null));
+    }
+}
diff --git 
a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMQueryHandler.java
 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMQueryHandler.java
new file mode 100644
index 0000000..59524e3
--- /dev/null
+++ 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMQueryHandler.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.query;
+
+import java.util.Optional;
+
+import org.apache.vysper.xmpp.server.ServerRuntimeContext;
+import org.apache.vysper.xmpp.server.SessionContext;
+import org.apache.vysper.xmpp.stanza.Stanza;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+public interface MAMQueryHandler {
+
+    /**
+     * @return Empty if this handler can't handle the provided query.
+     */
+    Optional<Stanza> handle(Query query, ServerRuntimeContext 
serverRuntimeContext, SessionContext sessionContext);
+
+}
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..9799a1e
--- /dev/null
+++ 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/Query.java
@@ -0,0 +1,71 @@
+/*
+ *  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 {
+
+    public static final String ELEMENT_NAME = "query";
+
+    private final IQStanza iqStanza;
+
+    private final XMLElement element;
+
+    public Query(IQStanza iqStanza) throws XMLSemanticError {
+        this.iqStanza = iqStanza;
+        this.element = iqStanza.getSingleInnerElementsNamed(ELEMENT_NAME);
+        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() + "'");
+        }
+    }
+
+    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);
+    }
+
+    public IQStanza iqStanza() {
+        return iqStanza;
+    }
+
+}
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/query/muc/MUCArchiveQueryHandler.java
 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/muc/MUCArchiveQueryHandler.java
new file mode 100644
index 0000000..9ae5dd2
--- /dev/null
+++ 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/muc/MUCArchiveQueryHandler.java
@@ -0,0 +1,55 @@
+/*
+ *  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.muc;
+
+import static java.util.Optional.ofNullable;
+
+import java.util.Optional;
+
+import org.apache.vysper.xmpp.addressing.Entity;
+import 
org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.MAMQueryHandler;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.Query;
+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.Stanza;
+import org.apache.vysper.xmpp.stanza.StanzaErrorCondition;
+import org.apache.vysper.xmpp.stanza.StanzaErrorType;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+public class MUCArchiveQueryHandler implements MAMQueryHandler {
+
+    @Override
+    public Optional<Stanza> handle(Query query, ServerRuntimeContext 
serverRuntimeContext,
+                                                                  
SessionContext sessionContext) {
+        Entity queriedEntity = 
ofNullable(query.iqStanza().getTo()).orElse(sessionContext.getInitiatingEntity());
+        if 
(serverRuntimeContext.getServerEnitity().getDomain().equals(queriedEntity.getDomain()))
 {
+            return Optional.empty();
+        }
+
+        // MUC archives is not yet implemented
+        Stanza stanza = 
ServerErrorResponses.getStanzaError(StanzaErrorCondition.FEATURE_NOT_IMPLEMENTED,
+                query.iqStanza(), StanzaErrorType.MODIFY,
+                "Multi-User Chat message archive feature is not yet 
implemented", null, null);
+        return Optional.of(stanza);
+    }
+}
diff --git 
a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/pubsub/PubsubNodeArchiveQueryHandler.java
 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/pubsub/PubsubNodeArchiveQueryHandler.java
new file mode 100644
index 0000000..8ca018a
--- /dev/null
+++ 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/pubsub/PubsubNodeArchiveQueryHandler.java
@@ -0,0 +1,51 @@
+/*
+ *  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.pubsub;
+
+import java.util.Optional;
+
+import 
org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.MAMQueryHandler;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.Query;
+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.Stanza;
+import org.apache.vysper.xmpp.stanza.StanzaErrorCondition;
+import org.apache.vysper.xmpp.stanza.StanzaErrorType;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+public class PubsubNodeArchiveQueryHandler implements MAMQueryHandler {
+
+    @Override
+    public Optional<Stanza> handle(Query query, ServerRuntimeContext 
serverRuntimeContext,
+                                                                  
SessionContext sessionContext) {
+        if (!query.getNode().isPresent()) {
+            return Optional.empty();
+        }
+
+        // PubSub node archives is not yet implemented
+        Stanza stanza = 
ServerErrorResponses.getStanzaError(StanzaErrorCondition.FEATURE_NOT_IMPLEMENTED,
+                query.iqStanza(), StanzaErrorType.CANCEL, "Pubsub node message 
archive feature is not yet implemented",
+                null, null);
+        return Optional.of(stanza);
+    }
+}
diff --git 
a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/user/UserArchiveQuery.java
 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/user/UserArchiveQuery.java
new file mode 100644
index 0000000..a2115e8
--- /dev/null
+++ 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/user/UserArchiveQuery.java
@@ -0,0 +1,52 @@
+/*
+ *  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.user;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+
+import org.apache.vysper.xml.fragment.XMLSemanticError;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.Query;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.X;
+import 
org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.ArchivedMessages;
+import 
org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.user.UserMessageArchive;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+class UserArchiveQuery {
+
+    private final Query query;
+
+    private final UserMessageArchive userMessageArchive;
+
+    UserArchiveQuery(UserMessageArchive userMessageArchive, Query query) {
+        this.userMessageArchive = requireNonNull(userMessageArchive);
+        this.query = requireNonNull(query);
+    }
+
+    ArchivedMessages execute() throws XMLSemanticError {
+        Optional<X> x = query.getX();
+
+        return null;
+    }
+
+}
diff --git 
a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/user/UserArchiveQueryHandler.java
 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/user/UserArchiveQueryHandler.java
new file mode 100644
index 0000000..911b920
--- /dev/null
+++ 
b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/user/UserArchiveQueryHandler.java
@@ -0,0 +1,87 @@
+/*
+ *  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.user;
+
+import static java.util.Objects.requireNonNull;
+import static java.util.Optional.ofNullable;
+
+import java.util.Optional;
+
+import org.apache.vysper.xml.fragment.XMLSemanticError;
+import org.apache.vysper.xmpp.addressing.Entity;
+import 
org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.MAMQueryHandler;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.Query;
+import 
org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.ArchivedMessages;
+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.server.response.ServerErrorResponses;
+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 UserArchiveQueryHandler implements MAMQueryHandler {
+
+    @Override
+    public Optional<Stanza> handle(Query query, ServerRuntimeContext 
serverRuntimeContext,
+            SessionContext sessionContext) {
+        try {
+            return Optional.of(doHandle(query, serverRuntimeContext, 
sessionContext));
+        } catch (XMLSemanticError xmlSemanticError) {
+            Stanza internalServerError = 
ServerErrorResponses.getStanzaError(StanzaErrorCondition.INTERNAL_SERVER_ERROR,
+                    query.iqStanza(), StanzaErrorType.CANCEL, null, null, 
null);
+            return Optional.of(internalServerError);
+        }
+    }
+
+    private Stanza doHandle(Query query, ServerRuntimeContext 
serverRuntimeContext, SessionContext sessionContext)
+            throws XMLSemanticError {
+        Entity initiatingEntity = sessionContext.getInitiatingEntity();
+        Entity archiveOwner = 
ofNullable(query.iqStanza().getTo()).orElse(initiatingEntity);
+        if 
(!sessionContext.getInitiatingEntity().getBareJID().equals(archiveOwner.getBareJID()))
 {
+            // The initiating user is trying to read the archive of another 
user
+            return 
ServerErrorResponses.getStanzaError(StanzaErrorCondition.FORBIDDEN, 
query.iqStanza(),
+                    StanzaErrorType.CANCEL,
+                    "Entity " + initiatingEntity + " is not allowed to query " 
+ archiveOwner + " message archives.",
+                    null, null);
+        }
+
+        UserMessageArchives archives = requireNonNull(
+                (UserMessageArchives) 
serverRuntimeContext.getStorageProvider(UserMessageArchives.class),
+                "Could not find an instance of " + UserMessageArchives.class);
+
+        Entity archiveOwnerBareJID = archiveOwner.getBareJID();
+        Optional<UserMessageArchive> archive = 
archives.retrieve(archiveOwnerBareJID);
+        if (!archive.isPresent()) {
+            return 
ServerErrorResponses.getStanzaError(StanzaErrorCondition.ITEM_NOT_FOUND, 
query.iqStanza(),
+                    StanzaErrorType.CANCEL, "No user message archive found for 
entity " + archiveOwnerBareJID, null,
+                    null);
+        }
+
+        ArchivedMessages archivedMessages = new 
UserArchiveQuery(archive.get(), query).execute();
+
+        // TODO
+        return null;
+    }
+}
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

Reply via email to