From 6fc81302b258b38203ad3777d63a07c5c15896f5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Blin?=
 <sebastien.blin@savoirfairelinux.com>
Date: Mon, 28 May 2018 14:59:11 -0400
Subject: [PATCH] sipaccountbase: add getLastMessages()

Because a client can receives messages before a client is ready to
receive it and because the daemon does not store any message, we
should have, like calls or file transfers, have a method to get
these messages.

So, this patch introduces a new method which gives the ability to
retrieve messages since the daemon has been launched. To do that,
when the daemon will receives a new message, it will store this
message into a queue (limited to 1000 messages) and save the
related timestamp. When the client is ready it can call getLastMessages
with its last known timestamp. This method will return all messages
received after this timestamp.

Note: the perfect way to do that is to store messages directly in
the daemon not clients. We also need some synchronization processes.

Change-Id: Iceb1654088a843f9be5b4a47bcc23201e9b38c01
---
 .../cx.ring.Ring.ConfigurationManager.xml     |  7 ++++
 bin/dbus/dbusconfigurationmanager.cpp         | 15 +++++++++
 bin/dbus/dbusconfigurationmanager.h           |  3 ++
 bin/jni/configurationmanager.i                |  9 ++++++
 bin/nodejs/configurationmanager.i             |  8 +++++
 src/account.h                                 |  7 +++-
 src/client/configurationmanager.cpp           |  6 ++++
 src/dring/configurationmanager_interface.h    |  8 +++++
 src/manager.cpp                               |  8 +++++
 src/manager.h                                 |  2 ++
 src/ringdht/ringaccount.cpp                   |  7 ++++
 src/ringdht/ringaccount.h                     |  6 ++++
 src/sip/sipaccountbase.cpp                    | 10 ++++++
 src/sip/sipaccountbase.h                      | 32 ++++++++++++++++---
 14 files changed, 122 insertions(+), 6 deletions(-)

diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
index d78238d274..83d2702cdc 100644
--- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
+++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
@@ -540,6 +540,13 @@
           </arg>
        </method>
 
+       <method name="getLastMessages" tp:name-for-bindings="getLastMessages">
+           <arg type="s" name="accountID" direction="in"/>
+           <arg type="t" name="base_timestamp" direction="in"/>
+           <arg type="a(sa{ss}t)" name="messages" direction="out"/>
+           <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="messages"/>
+        </method>
+
        <method name="getMessageStatus" tp:name-for-bindings="getMessageStatus">
           <arg type="t" name="id" direction="in"/>
           <arg type="i" name="status" direction="out">
diff --git a/bin/dbus/dbusconfigurationmanager.cpp b/bin/dbus/dbusconfigurationmanager.cpp
index 0d11510b0a..1e30e75ecb 100644
--- a/bin/dbus/dbusconfigurationmanager.cpp
+++ b/bin/dbus/dbusconfigurationmanager.cpp
@@ -153,6 +153,21 @@ DBusConfigurationManager::sendTextMessage(const std::string& accountID, const st
     return DRing::sendAccountTextMessage(accountID, to, payloads);
 }
 
+std::vector<RingDBusMessage>
+DBusConfigurationManager::getLastMessages(const std::string& accountID, const uint64_t& base_timestamp)
+{
+    auto messages = DRing::getLastMessages(accountID, base_timestamp);
+    std::vector<RingDBusMessage> result;
+    for (const auto& message : messages) {
+        RingDBusMessage m;
+        m._1 = message.from;
+        m._2 = message.payloads;
+        m._3 = message.received;
+        result.emplace_back(m);
+    }
+    return result;
+}
+
 auto
 DBusConfigurationManager::getMessageStatus(const uint64_t& id) -> decltype(DRing::getMessageStatus(id))
 {
diff --git a/bin/dbus/dbusconfigurationmanager.h b/bin/dbus/dbusconfigurationmanager.h
index 2d76ab8d2b..894241d2cf 100644
--- a/bin/dbus/dbusconfigurationmanager.h
+++ b/bin/dbus/dbusconfigurationmanager.h
@@ -49,6 +49,8 @@
 #pragma GCC diagnostic warning "-Wunused-but-set-variable"
 #endif
 
+using RingDBusMessage = DBus::Struct<std::string, std::map<std::string, std::string>, long unsigned int>;
+
 class DBusConfigurationManager :
     public cx::ring::Ring::ConfigurationManager_adaptor,
     public DBus::IntrospectableAdaptor,
@@ -80,6 +82,7 @@ class DBusConfigurationManager :
         void sendRegister(const std::string& accoundID, const bool& enable);
         void registerAllAccounts(void);
         uint64_t sendTextMessage(const std::string& accoundID, const std::string& to, const std::map<std::string, std::string>& payloads);
+        std::vector<RingDBusMessage> getLastMessages(const std::string& accountID, const uint64_t& base_timestamp);
         int getMessageStatus(const uint64_t& id);
         std::map<std::string, std::string> getTlsDefaultSettings();
         std::vector<std::string> getSupportedCiphers(const std::string& accountID);
diff --git a/bin/jni/configurationmanager.i b/bin/jni/configurationmanager.i
index 71179f58a6..600ac6315d 100644
--- a/bin/jni/configurationmanager.i
+++ b/bin/jni/configurationmanager.i
@@ -60,9 +60,17 @@ public:
 %}
 
 %feature("director") ConfigurationCallback;
+%template(MessageVect) std::vector<DRing::Message>;
 
 namespace DRing {
 
+struct Message
+{
+    std::string from;
+    std::map<std::string, std::string> payloads;
+    uint64_t received;
+};
+
 std::map<std::string, std::string> getAccountDetails(const std::string& accountID);
 std::map<std::string, std::string> getVolatileAccountDetails(const std::string& accountID);
 void setAccountDetails(const std::string& accountID, const std::map<std::string, std::string>& details);
@@ -74,6 +82,7 @@ std::vector<std::string> getAccountList();
 void sendRegister(const std::string& accountID, bool enable);
 void registerAllAccounts(void);
 uint64_t sendAccountTextMessage(const std::string& accountID, const std::string& to, const std::map<std::string, std::string>& message);
+std::vector<DRing::Message> getLastMessages(const std::string& accountID, uint64_t base_timestamp);
 int getMessageStatus(uint64_t id);
 
 bool lookupName(const std::string& account, const std::string& nameserver, const std::string& name);
diff --git a/bin/nodejs/configurationmanager.i b/bin/nodejs/configurationmanager.i
index c56c99e81e..5170e1f239 100644
--- a/bin/nodejs/configurationmanager.i
+++ b/bin/nodejs/configurationmanager.i
@@ -60,6 +60,13 @@ public:
 
 namespace DRing {
 
+struct Message
+{
+    std::string from;
+    std::map<std::string, std::string> payloads;
+    uint64_t received;
+};
+
 std::map<std::string, std::string> getAccountDetails(const std::string& accountID);
 std::map<std::string, std::string> getVolatileAccountDetails(const std::string& accountID);
 void setAccountDetails(const std::string& accountID, const std::map<std::string, std::string>& details);
@@ -71,6 +78,7 @@ std::vector<std::string> getAccountList();
 void sendRegister(const std::string& accountID, bool enable);
 void registerAllAccounts(void);
 uint64_t sendAccountTextMessage(const std::string& accountID, const std::string& to, const std::map<std::string, std::string>& message);
+std::vector<Message> getLastMessages(const std::string& accountID, const uint64_t& base_timestamp);
 int getMessageStatus(uint64_t id);
 
 bool lookupName(const std::string& account, const std::string& nameserver, const std::string& name);
diff --git a/src/account.h b/src/account.h
index c71d8e55da..027b429ca3 100644
--- a/src/account.h
+++ b/src/account.h
@@ -27,6 +27,7 @@
 #include "config.h"
 #endif
 
+#include "configurationmanager_interface.h"
 #include "noncopyable.h"
 #include "config/serializable.h"
 #include "registration_states.h"
@@ -34,7 +35,7 @@
 #include "ip_utils.h"
 #include "media_codec.h"
 #include "logger.h"
-#include "compiler_intrinsics.h" // UNUSED
+#include "compiler_intrinsics.h" // include the "UNUSED" macro
 
 #include <functional>
 #include <string>
@@ -155,6 +156,10 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
         virtual uint64_t sendTextMessage(const std::string& to UNUSED,
                                      const std::map<std::string, std::string>& payloads UNUSED) { return 0; }
 
+        virtual std::vector<DRing::Message> getLastMessages(const uint64_t& base_timestamp) {
+            return {};
+        }
+
         /**
          * Return the status corresponding to the token.
          */
diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp
index 1f4d11c341..6cb22bd542 100644
--- a/src/client/configurationmanager.cpp
+++ b/src/client/configurationmanager.cpp
@@ -270,6 +270,12 @@ sendAccountTextMessage(const std::string& accountID, const std::string& to, cons
     return ring::Manager::instance().sendTextMessage(accountID, to, payloads);
 }
 
+std::vector<Message>
+getLastMessages(const std::string& accountID, const uint64_t& base_timestamp)
+{
+    return ring::Manager::instance().getLastMessages(accountID, base_timestamp);
+}
+
 int
 getMessageStatus(uint64_t id)
 {
diff --git a/src/dring/configurationmanager_interface.h b/src/dring/configurationmanager_interface.h
index 1e107cbe49..971e081eac 100644
--- a/src/dring/configurationmanager_interface.h
+++ b/src/dring/configurationmanager_interface.h
@@ -40,6 +40,13 @@
 
 namespace DRing {
 
+struct Message
+{
+    std::string from;
+    std::map<std::string, std::string> payloads;
+    uint64_t received;
+};
+
 void registerConfHandlers(const std::map<std::string, std::shared_ptr<CallbackWrapperBase>>&);
 
 std::map<std::string, std::string> getAccountDetails(const std::string& accountID);
@@ -65,6 +72,7 @@ std::vector<std::string> getAccountList();
 void sendRegister(const std::string& accountID, bool enable);
 void registerAllAccounts(void);
 uint64_t sendAccountTextMessage(const std::string& accountID, const std::string& to, const std::map<std::string, std::string>& payloads);
+std::vector<Message> getLastMessages(const std::string& accountID, const uint64_t& base_timestamp);
 int getMessageStatus(uint64_t id);
 
 
diff --git a/src/manager.cpp b/src/manager.cpp
index 40e7081a16..2b42471b58 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -3150,4 +3150,12 @@ Manager::getVideoManager() const
 }
 #endif
 
+std::vector<DRing::Message>
+Manager::getLastMessages(const std::string& accountID, const uint64_t& base_timestamp)
+{
+    if (const auto acc = getAccount(accountID))
+        return acc->getLastMessages(base_timestamp);
+    return {};
+}
+
 } // namespace ring
diff --git a/src/manager.h b/src/manager.h
index 5f35d2619c..23df1ac6f2 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -893,6 +893,8 @@ class Manager {
 
         std::unique_ptr<DataTransferFacade> dataTransfers;
 
+        std::vector<DRing::Message> getLastMessages(const std::string& accountID, const uint64_t& base_timestamp);
+
 private:
         Manager();
         ~Manager();
diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp
index a5d84e69d2..22dfc80d0a 100644
--- a/src/ringdht/ringaccount.cpp
+++ b/src/ringdht/ringaccount.cpp
@@ -3457,6 +3457,7 @@ void RingAccount::pushNotificationReceived(const std::string& from, const std::m
     dht_.pushNotificationReceived(data);
 }
 
+
 std::string
 RingAccount::getUserUri() const
 {
@@ -3467,4 +3468,10 @@ RingAccount::getUserUri() const
     return username_;
 }
 
+
+std::vector<DRing::Message>
+RingAccount::getLastMessages(const uint64_t& base_timestamp)
+{
+    return SIPAccountBase::getLastMessages(base_timestamp);
+}
 } // namespace ring
diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h
index d6021a78c4..bd99f8dda2 100644
--- a/src/ringdht/ringaccount.h
+++ b/src/ringdht/ringaccount.h
@@ -378,6 +378,12 @@ class RingAccount : public SIPAccountBase {
 
         std::string getUserUri() const override;
 
+        /**
+         * Get last messages (should be used to retrieve messages when launching the client)
+         * @param base_timestamp
+         */
+        std::vector<DRing::Message> getLastMessages(const uint64_t& base_timestamp);
+
     private:
         NON_COPYABLE(RingAccount);
 
diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp
index 4457fadbe1..0799e571cd 100644
--- a/src/sip/sipaccountbase.cpp
+++ b/src/sip/sipaccountbase.cpp
@@ -42,6 +42,7 @@
 #include "fileutils.h"
 #include "sip_utils.h"
 
+#include <ctime>
 #include <type_traits>
 
 namespace ring {
@@ -386,6 +387,15 @@ SIPAccountBase::onTextMessage(const std::string& from,
 {
     RING_DBG("Text message received from %s, %zu part(s)",  from.c_str(), payloads.size());
     emitSignal<DRing::ConfigurationSignal::IncomingAccountMessage>(accountID_, from, payloads);
+    DRing::Message message;
+    message.from = from;
+    message.payloads = payloads;
+    message.received = std::time(nullptr);
+    std::lock_guard<std::mutex> lck(mutexLastMessages_);
+    lastMessages_.emplace_back(message);
+    while (lastMessages_.size() > MAX_WAITING_MESSAGES_SIZE) {
+        lastMessages_.pop_front();
+    }
 }
 
 void
diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h
index a1ecedcb30..181269890d 100644
--- a/src/sip/sipaccountbase.h
+++ b/src/sip/sipaccountbase.h
@@ -18,8 +18,7 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
-#ifndef SIPACCOUNTBASE_H
-#define SIPACCOUNTBASE_H
+#pragma once
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -35,9 +34,11 @@
 #include <pjsip/sip_types.h>
 
 #include <array>
-#include <vector>
+#include <deque>
 #include <map>
 #include <memory>
+#include <mutex>
+#include <vector>
 
 #ifdef _WIN32
 typedef uint16_t in_port_t;
@@ -276,6 +277,19 @@ public:
 
     virtual std::string getUserUri() const = 0;
 
+    std::vector<DRing::Message> getLastMessages(const uint64_t& base_timestamp) {
+        std::lock_guard<std::mutex> lck(mutexLastMessages_);
+        auto it = lastMessages_.begin();
+        size_t num = lastMessages_.size();
+        while (it != lastMessages_.end() and it->received <= base_timestamp) {
+            num--;
+            ++it;
+        }
+        if (num == 0)
+            return {};
+        return {it, lastMessages_.end()};
+    }
+
 public: // overloaded methods
     virtual void flush() override;
 
@@ -416,11 +430,19 @@ protected:
     uint16_t getRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const;
     uint16_t acquireRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const;
 
+    /**
+     * The deamon can be launched without any client (or with a non ready client)
+     * Like call and file transfer, a client should be able to retrieve current messages.
+     * To avoid to explode the size in memory, this container should be limited.
+     * We don't want to see monsters in memory.
+     */
+    std::mutex mutexLastMessages_;
+    static constexpr size_t MAX_WAITING_MESSAGES_SIZE = 1000;
+    std::deque<DRing::Message> lastMessages_;
+
 private:
     NON_COPYABLE(SIPAccountBase);
 
 };
 
 } // namespace ring
-
-#endif
-- 
GitLab