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
This commit is contained in:
Sébastien Blin
2018-05-28 14:59:11 -04:00
parent c21c5b779e
commit 6fc81302b2
14 changed files with 122 additions and 6 deletions

View File

@ -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">

View File

@ -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))
{

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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.
*/

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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