Commit 1dccd81b authored by Adrien Béraud's avatar Adrien Béraud

multi-device: initial implementation

* generate device certificate
* generate encrypted archive including:
 - Account certificate
 - Generated Ethereum key
* adds "signed receipt" to the account configuration
* adds mechanism to export/import account over the DHT

Change-Id: Iac77f990b094c3405785fb0df41bc8a993995a26
Tuleap: #938
parent 4e4a609d
......@@ -154,6 +154,44 @@
</arg>
</method>
<method name="exportOnRing" tp:name-for-bindings="exportOnRing">
<tp:docstring>
Export account on the DHT using the given password and generated PIN (returned through exportOnRingEnded signal).
</tp:docstring>
<arg type="s" name="accountID" direction="in">
</arg>
<arg type="s" name="password" direction="in">
</arg>
<arg type="b" name="success" direction="out">
<tp:docstring>
True if the operation was initialized successfully. exportOnRingEnded will be trigered on completion.
</tp:docstring>
</arg>
</method>
<signal name="exportOnRingEnded" tp:name-for-bindings="exportOnRingEnded">
<tp:docstring>
Notify clients when the exportOnRing operation ended.
</tp:docstring>
<arg type="s" name="accountID">
</arg>
<arg type="i" name="status">
<tp:docstring>
Status code: 0 for success
<ul>
<li>SUCCESS = 0 everything went fine. PIN is set.</li>
<li>WRONG_PASSWORD = 1 wrong password provided.</li>
<li>NETWORK_ERROR = 2 can't publish archive on the network.</li>
</ul>
</tp:docstring>
</arg>
<arg type="s" name="PIN">
<tp:docstring>
A PIN to show to the user to import the account from somewhere else.
</tp:docstring>
</arg>
</signal>
<method name="testAccountICEInitialization" tp:name-for-bindings="testAccountICEInitialization">
<tp:docstring>
Test initializing an ICE transport with the current account configuration.
......
......@@ -176,6 +176,7 @@ DBusClient::initLibrary(int flags)
exportable_callback<ConfigurationSignal::IncomingAccountMessage>(bind(&DBusConfigurationManager::incomingAccountMessage, confM, _1, _2, _3 )),
exportable_callback<ConfigurationSignal::AccountMessageStatusChanged>(bind(&DBusConfigurationManager::accountMessageStatusChanged, confM, _1, _2, _3, _4 )),
exportable_callback<ConfigurationSignal::IncomingTrustRequest>(bind(&DBusConfigurationManager::incomingTrustRequest, confM, _1, _2, _3, _4 )),
exportable_callback<ConfigurationSignal::ExportOnRingEnded>(bind(&DBusConfigurationManager::exportOnRingEnded, confM, _1, _2, _3 )),
exportable_callback<ConfigurationSignal::CertificatePinned>(bind(&DBusConfigurationManager::certificatePinned, confM, _1 )),
exportable_callback<ConfigurationSignal::CertificatePathPinned>(bind(&DBusConfigurationManager::certificatePathPinned, confM, _1, _2 )),
exportable_callback<ConfigurationSignal::CertificateExpired>(bind(&DBusConfigurationManager::certificateExpired, confM, _1 )),
......
......@@ -74,6 +74,12 @@ DBusConfigurationManager::addAccount(const std::map<std::string, std::string>& d
return DRing::addAccount(details);
}
auto
DBusConfigurationManager::exportOnRing(const std::string& accountID, const std::string& password) -> decltype(DRing::exportOnRing(accountID, password))
{
return DRing::exportOnRing(accountID, password);
}
void
DBusConfigurationManager::removeAccount(const std::string& accountID)
{
......
......@@ -63,6 +63,7 @@ class DBusConfigurationManager :
void setAccountActive(const std::string& accountID, const bool& active);
std::map<std::string, std::string> getAccountTemplate(const std::string& accountType);
std::string addAccount(const std::map<std::string, std::string>& details);
bool exportOnRing(const std::string& accountID, const std::string& password);
void removeAccount(const std::string& accoundID);
std::vector<std::string> getAccountList();
void sendRegister(const std::string& accoundID, const bool& enable);
......
# MSGPACK
MSGPACK_VERSION := 068041f05eb1b8ab2930a7679dfe89ba7d14cb79
MSGPACK_VERSION := 1df97bc37b363a340c5ad06c5cbcc53310aaff80
MSGPACK_URL := https://github.com/msgpack/msgpack-c/archive/$(MSGPACK_VERSION).tar.gz
PKGS += msgpack
......
# OPENDHT
OPENDHT_VERSION := 0.6.3
OPENDHT_VERSION := aaed64039a40e7d1bc7a8907c53284506484aefb
OPENDHT_URL := https://github.com/savoirfairelinux/opendht/archive/$(OPENDHT_VERSION).tar.gz
PKGS += opendht
......
......@@ -130,7 +130,9 @@ libring_la_SOURCES = \
ring_api.cpp \
rational.h \
smartools.cpp \
smartools.h
smartools.h \
base64.h \
base64.cpp
if HAVE_WIN32
libring_la_SOURCES += \
......
......@@ -213,7 +213,6 @@ Account::serialize(YAML::Emitter& out)
out << YAML::Key << RINGTONE_PATH_KEY << YAML::Value << ringtonePath_;
out << YAML::Key << HAS_CUSTOM_USER_AGENT_KEY << YAML::Value << hasCustomUserAgent_;
out << YAML::Key << USER_AGENT_KEY << YAML::Value << userAgent_;
out << YAML::Key << USERNAME_KEY << YAML::Value << username_;
out << YAML::Key << DISPLAY_NAME_KEY << YAML::Value << displayName_;
out << YAML::Key << HOSTNAME_KEY << YAML::Value << hostname_;
out << YAML::Key << UPNP_ENABLED_KEY << YAML::Value << upnpEnabled_;
......@@ -226,7 +225,6 @@ Account::unserialize(const YAML::Node& node)
parseValue(node, ALIAS_KEY, alias_);
parseValue(node, ACCOUNT_ENABLE_KEY, enabled_);
parseValue(node, USERNAME_KEY, username_);
parseValue(node, ACCOUNT_AUTOANSWER_KEY, autoAnswerEnabled_);
parseValue(node, ACCOUNT_ACTIVE_CALL_LIMIT_KEY, activeCallLimit_);
//parseValue(node, PASSWORD_KEY, password_);
......@@ -257,7 +255,6 @@ Account::setAccountDetails(const std::map<std::string, std::string> &details)
parseString(details, Conf::CONFIG_ACCOUNT_ALIAS, alias_);
parseString(details, Conf::CONFIG_ACCOUNT_DISPLAYNAME, displayName_);
parseBool(details, Conf::CONFIG_ACCOUNT_ENABLE, enabled_);
parseString(details, Conf::CONFIG_ACCOUNT_USERNAME, username_);
parseString(details, Conf::CONFIG_ACCOUNT_HOSTNAME, hostname_);
parseString(details, Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_);
parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, userAgent_);
......@@ -366,6 +363,8 @@ Account::mapStateNumberToString(RegistrationState state)
CASE_STATE(ERROR_SERVICE_UNAVAILABLE);
CASE_STATE(ERROR_EXIST_STUN);
CASE_STATE(ERROR_NOT_ACCEPTABLE);
CASE_STATE(ERROR_NEED_MIGRATION);
CASE_STATE(INITIALIZING);
default:
return DRing::Account::States::ERROR_GENERIC;
}
......
......@@ -134,6 +134,8 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
*/
virtual void doUnregister(std::function<void(bool)> cb = std::function<void(bool)>()) = 0;
RegistrationState getRegistrationState() const { return registrationState_; }
/**
* Create a new outgoing call.
*
......
......@@ -20,8 +20,6 @@
#include "archiver.h"
#include <json/json.h>
#include "client/ring_signal.h"
#include "account_const.h"
#include "configurationmanager_interface.h"
......@@ -31,27 +29,67 @@
#include "logger.h"
#include <opendht/crypto.h>
#include <json/json.h>
#include <zlib.h>
#include <fstream>
#include <sys/stat.h>
#include <fstream>
namespace ring {
namespace archiver {
Archiver&
Archiver::instance()
{
// Meyers singleton
static Archiver instance_;
return instance_;
std::map<std::string, std::string>
jsonValueToAccount(Json::Value& value, const std::string& accountId) {
auto idPath_ = fileutils::get_data_dir() + DIR_SEPARATOR_STR + accountId;
fileutils::check_dir(idPath_.c_str(), 0700);
auto detailsMap = DRing::getAccountTemplate(value[DRing::Account::ConfProperties::TYPE].asString());
for( Json::ValueIterator itr = value.begin() ; itr != value.end() ; itr++ ) {
if (itr->asString().empty())
continue;
if (itr.key().asString().compare(DRing::Account::ConfProperties::TLS::CA_LIST_FILE) == 0) {
std::string fileContent(itr->asString());
fileutils::saveFile(idPath_ + DIR_SEPARATOR_STR "ca.key", {fileContent.begin(), fileContent.end()}, 0600);
} else if (itr.key().asString().compare(DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) == 0) {
std::string fileContent(itr->asString());
fileutils::saveFile(idPath_ + DIR_SEPARATOR_STR "dht.key", {fileContent.begin(), fileContent.end()}, 0600);
} else if (itr.key().asString().compare(DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE) == 0) {
std::string fileContent(itr->asString());
fileutils::saveFile(idPath_ + DIR_SEPARATOR_STR "dht.crt", {fileContent.begin(), fileContent.end()}, 0600);
} else
detailsMap[itr.key().asString()] = itr->asString();
}
return detailsMap;
}
Archiver::Archiver()
{
Json::Value
accountToJsonValue(std::map<std::string, std::string> details) {
Json::Value root;
std::map<std::string, std::string>::iterator iter;
for (iter = details.begin(); iter != details.end(); ++iter) {
if (iter->first.compare(DRing::Account::ConfProperties::Ringtone::PATH) == 0) {
// Ringtone path is not exportable
} else if (iter->first.compare(DRing::Account::ConfProperties::TLS::CA_LIST_FILE) == 0 ||
iter->first.compare(DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE) == 0 ||
iter->first.compare(DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) == 0) {
// replace paths by the files content
std::ifstream ifs(iter->second);
std::string fileContent((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
root[iter->first] = fileContent;
} else
root[iter->first] = iter->second;
}
return root;
}
int
Archiver::exportAccounts(std::vector<std::string> accountIDs,
exportAccounts(std::vector<std::string> accountIDs,
std::string filepath,
std::string password)
{
......@@ -109,31 +147,8 @@ Archiver::exportAccounts(std::vector<std::string> accountIDs,
return 0;
}
Json::Value
Archiver::accountToJsonValue(std::map<std::string, std::string> details) {
Json::Value root;
std::map<std::string, std::string>::iterator iter;
for (iter = details.begin(); iter != details.end(); ++iter) {
if (iter->first.compare(DRing::Account::ConfProperties::Ringtone::PATH) == 0) {
// Ringtone path is not exportable
} else if (iter->first.compare(DRing::Account::ConfProperties::TLS::CA_LIST_FILE) == 0 ||
iter->first.compare(DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE) == 0 ||
iter->first.compare(DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) == 0) {
// replace paths by the files content
std::ifstream ifs(iter->second);
std::string fileContent((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
root[iter->first] = fileContent;
} else
root[iter->first] = iter->second;
}
return root;
}
int
Archiver::importAccounts(std::string archivePath, std::string password)
importAccounts(std::string archivePath, std::string password)
{
if (archivePath.empty()) {
RING_ERR("Missing arguments");
......@@ -191,35 +206,8 @@ Archiver::importAccounts(std::string archivePath, std::string password)
return 0;
}
std::map<std::string, std::string>
Archiver::jsonValueToAccount(Json::Value& value, const std::string& accountId) {
auto idPath_ = fileutils::get_data_dir() + DIR_SEPARATOR_STR + accountId;
fileutils::check_dir(idPath_.c_str(), 0700);
auto detailsMap = DRing::getAccountTemplate(value[DRing::Account::ConfProperties::TYPE].asString());
for( Json::ValueIterator itr = value.begin() ; itr != value.end() ; itr++ ) {
if (itr->asString().empty())
continue;
if (itr.key().asString().compare(DRing::Account::ConfProperties::TLS::CA_LIST_FILE) == 0) {
std::string fileContent(itr->asString());
fileutils::saveFile(idPath_ + DIR_SEPARATOR_STR "ca.key", {fileContent.begin(), fileContent.end()}, 0600);
} else if (itr.key().asString().compare(DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) == 0) {
std::string fileContent(itr->asString());
fileutils::saveFile(idPath_ + DIR_SEPARATOR_STR "dht.key", {fileContent.begin(), fileContent.end()}, 0600);
} else if (itr.key().asString().compare(DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE) == 0) {
std::string fileContent(itr->asString());
fileutils::saveFile(idPath_ + DIR_SEPARATOR_STR "dht.crt", {fileContent.begin(), fileContent.end()}, 0600);
} else
detailsMap[itr.key().asString()] = itr->asString();
}
return detailsMap;
}
std::vector<uint8_t>
Archiver::compress(const std::string& str, int compressionlevel)
compress(const std::string& str)
{
auto destSize = compressBound(str.size());
std::vector<uint8_t> outbuffer(destSize);
......@@ -229,20 +217,20 @@ Archiver::compress(const std::string& str, int compressionlevel)
if (ret != Z_OK) {
std::ostringstream oss;
oss << "Exception during zlib compression: (" << ret << ") ";
throw(std::runtime_error(oss.str()));
throw std::runtime_error(oss.str());
}
return outbuffer;
}
std::vector<uint8_t>
Archiver::decompress(const std::vector<uint8_t>& str)
decompress(const std::vector<uint8_t>& str)
{
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
if (inflateInit(&zs) != Z_OK)
throw(std::runtime_error("inflateInit failed while decompressing."));
throw std::runtime_error("inflateInit failed while decompressing.");
zs.next_in = (Bytef*)str.data();
zs.avail_in = str.size();
......@@ -278,4 +266,4 @@ Archiver::decompress(const std::vector<uint8_t>& str)
return out;
}
} // namespace ring
}} // namespace ring::archiver
......@@ -25,59 +25,45 @@
#include <string>
#include <vector>
#include <map>
#include <zlib.h>
namespace Json {
class Value;
};
namespace ring {
/**
* Archiver is used to generate/read encrypted archives
*/
class Archiver {
public:
static Archiver& instance();
Archiver();
/**
* Create a protected archive containing a list of accounts
* @param accountIDs The accounts to exports
* @param filepath The filepath where to put the resulting archive
* @param password The mandatory password to set on the archive
* @returns 0 for OK, error code otherwise
*/
int exportAccounts(std::vector<std::string> accountIDs,
std::string filepath,
std::string password);
namespace archiver {
/**
* Read a protected archive and add accounts found in it
* Warning: this function must be called from a registered pjsip thread
* @param archivePath The path to the archive file
* @param password The password to read the archive
* @returns 0 for OK, error code otherwise
*/
int importAccounts(std::string archivePath, std::string password);
/**
* Create a protected archive containing a list of accounts
* @param accountIDs The accounts to exports
* @param filepath The filepath where to put the resulting archive
* @param password The mandatory password to set on the archive
* @returns 0 for OK, error code otherwise
*/
int exportAccounts(std::vector<std::string> accountIDs,
std::string filepath,
std::string password);
/**
* Compress a STL string using zlib with given compression level and return
* the binary data.
*/
std::vector<uint8_t> compress(const std::string& str, int compressionlevel = Z_BEST_COMPRESSION);
/**
* Read a protected archive and add accounts found in it
* Warning: this function must be called from a registered pjsip thread
* @param archivePath The path to the archive file
* @param password The password to read the archive
* @returns 0 for OK, error code otherwise
*/
int importAccounts(std::string archivePath, std::string password);
/**
* Decompress an STL string using zlib and return the original data.
*/
std::vector<uint8_t> decompress(const std::vector<uint8_t>& dat);
/**
* Compress a STL string using zlib with given compression level and return
* the binary data.
*/
std::vector<uint8_t> compress(const std::string& str);
private:
NON_COPYABLE(Archiver);
/**
* Decompress an STL string using zlib and return the original data.
*/
std::vector<uint8_t> decompress(const std::vector<uint8_t>& dat);
static Json::Value accountToJsonValue(std::map<std::string, std::string> details);
static std::map<std::string, std::string> jsonValueToAccount(Json::Value& value, const std::string& accountId);
};
}
} // namespace ring
......@@ -16,6 +16,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "base64.h"
#include <stdint.h>
#include <stdlib.h>
......@@ -109,3 +111,38 @@ uint8_t *ring_base64_decode(const char *input, size_t input_length,
return output;
}
namespace ring {
namespace base64 {
std::string
encode(const std::vector<uint8_t>::const_iterator begin,
const std::vector<uint8_t>::const_iterator end)
{
size_t output_length = 4 * ((std::distance(begin, end) + 2) / 3);
std::string out;
out.resize(output_length);
ring_base64_encode(&(*begin), std::distance(begin, end),
&(*out.begin()), &output_length);
out.resize(output_length);
return out;
}
std::string
encode(const std::vector<uint8_t>& dat)
{
return encode(dat.cbegin(), dat.cend());
}
std::vector<uint8_t>
decode(const std::string& str)
{
size_t output_length = str.length() / 4 * 3 + 2;
std::vector<uint8_t> output;
output.resize(output_length);
ring_base64_decode(str.data(), str.size(), output.data(), &output_length);
output.resize(output_length);
return output;
}
}} // namespace ring::base64
......@@ -16,14 +16,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef H_BASE64
#define H_BASE64
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "stdint.h"
#include <stdint.h>
#include <stddef.h>
/**
* Encode a buffer in base64.
......@@ -51,46 +47,14 @@ char *ring_base64_encode(const uint8_t *input, size_t input_length,
uint8_t *ring_base64_decode(const char *input, size_t input_length,
uint8_t *output, size_t *output_length);
#ifdef __cplusplus
}
#endif
#include <string>
#include <vector>
namespace ring {
namespace base64 {
std::string
encode(const std::vector<uint8_t>::const_iterator begin,
const std::vector<uint8_t>::const_iterator end)
{
size_t output_length = 4 * ((std::distance(begin, end) + 2) / 3);
std::string out;
out.resize(output_length);
ring_base64_encode(&(*begin), std::distance(begin, end),
&(*out.begin()), &output_length);
out.resize(output_length);
return out;
}
std::string
encode(const std::vector<uint8_t>& dat)
{
return encode(dat.cbegin(), dat.cend());
}
std::vector<uint8_t>
decode(const std::string& str)
{
size_t output_length = str.length() / 4 * 3 + 2;
std::vector<uint8_t> output;
output.resize(output_length);
ring_base64_decode(str.data(), str.size(), output.data(), &output_length);
output.resize(output_length);
return output;
}
std::string encode(const std::vector<uint8_t>::const_iterator begin, const std::vector<uint8_t>::const_iterator end);
std::string encode(const std::vector<uint8_t>& dat);
std::vector<uint8_t> decode(const std::string& str);
}} // namespace ring::base64
#endif // H_BASE64
......@@ -158,10 +158,24 @@ Call::setState(CallState call_state, ConnectionState cnx_state, signed code)
connectionState_ = cnx_state;
auto new_client_state = getStateStr();
if (call_state == CallState::OVER) {
RING_DBG("[call:%s] %lu subcalls", id_.c_str(), subcalls.size());
if (not subcalls.empty()) {
auto subs = std::move(subcalls);
for (auto c : subs)
c->hangup(0);
}
}
for (auto& l : stateChangedListeners_)
l(callState_, connectionState_, code);
if (old_client_state != new_client_state) {
RING_DBG("[call:%s] emit client call state change %s, code %d",
id_.c_str(), new_client_state.c_str(), code);
emitSignal<DRing::CallSignal::StateChange>(id_, new_client_state, code);
if (not quiet) {
RING_DBG("[call:%s] emit client call state change %s, code %d",
id_.c_str(), new_client_state.c_str(), code);
emitSignal<DRing::CallSignal::StateChange>(id_, new_client_state, code);
}
}
return true;
......@@ -354,4 +368,87 @@ Call::peerHungup()
aborted ? ECONNABORTED : ECONNREFUSED);
}
void
Call::addSubCall(const std::shared_ptr<Call>& call)
{
if (connectionState_ == ConnectionState::CONNECTED
|| callState_ == CallState::ACTIVE
|| callState_ == CallState::OVER) {
call->removeCall();
} else {
std::lock_guard<std::recursive_mutex> lk (callMutex_);
if (not subcalls.emplace(call).second)
return;
call->quiet = true;
std::weak_ptr<Call> wthis = shared_from_this();
std::weak_ptr<Call> wcall = call;
call->addStateListener([wcall,wthis](Call::CallState new_state, Call::ConnectionState new_cstate, int code) {
if (auto call = wcall.lock()) {
if (auto sthis = wthis.lock()) {
auto& this_ = *sthis;
auto sit = this_.subcalls.find(call);
if (sit == this_.subcalls.end())
return;
RING_WARN("[call %s] DeviceCall call %s state changed %d %d", this_.getCallId().c_str(), call->getCallId().c_str(), new_state, new_cstate);
if (new_state == CallState::OVER) {
std::lock_guard<std::recursive_mutex> lk (this_.callMutex_);
this_.subcalls.erase(call);
} else if (new_state == CallState::ACTIVE && this_.callState_ == CallState::INACTIVE) {
this_.setState(new_state);
}
if ((unsigned)this_.connectionState_ < (unsigned)new_cstate && (unsigned)new_cstate < (unsigned)ConnectionState::RINGING) {
this_.setState(new_cstate);
} else if (new_cstate == ConnectionState::DISCONNECTED && new_state == CallState::ACTIVE) {
std::lock_guard<std::recursive_mutex> lk (this_.callMutex_);
RING_WARN("[call %s] peer hangup", this_.getCallId().c_str());
auto subcalls = std::move(this_.subcalls);
for (auto& sub : subcalls) {
if (sub != call)
try {
sub->hangup(0);
} catch(const std::exception& e) {
RING_WARN("[call %s] error hanging up: %s", this_.getCallId().c_str());
}
}
this_.peerHungup();
}
if (new_state == CallState::ACTIVE && new_cstate == ConnectionState::CONNECTED) {
std::lock_guard<std::recursive_mutex> lk (this_.callMutex_);
RING_WARN("[call %s] peer answer", this_.getCallId().c_str());
auto subcalls = std::move(this_.subcalls);
for (auto& sub : subcalls) {
if (sub != call)
sub->hangup(0);
}
this_.merge(call);
}
RING_WARN("[call %s] Remaining %d subcalls", this_.getCallId().c_str(), this_.subcalls.size());
} else {
RING_WARN("DeviceCall IGNORED call %s state changed %d %d", call->getCallId().c_str(), new_state, new_cstate);
}
}
});
setState(ConnectionState::TRYING);
}
}
void
Call::merge(std::shared_ptr<Call> scall)
{
RING_WARN("[call %s] merge to -> [call %s]", scall->getCallId().c_str(), getCallId().c_str());
auto& call = *scall;
std::lock(callMutex_, call.callMutex_);
std::lock_guard<std::recursive_mutex> lk1 (callMutex_, std::adopt_lock);
std::lock_guard<std::recursive_mutex> lk2 (call.callMutex_, std::adopt_lock);
iceTransport_ = std::move(call.iceTransport_);
peerDisplayName_ = std::move(call.peerDisplayName_);
localAddr_ = call.localAddr_;
localAudioPort_ = call.localAudioPort_;
localVideoPort_ = call.localVideoPort_;
setState(call.getState());
setState(call.getConnectionState());
scall->removeCall();
}
} // namespace ring
......@@ -21,8 +21,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __CALL_H__
#define __CALL_H__
#pragma once
#ifdef HAVE_CONFIG_H
#include "config.h"
......@@ -40,6 +39,7 @@
#include <memory>
#include <vector>
#include <condition_variable>
#include <set>
namespace ring {
......@@ -330,9 +330,18 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> {
}
virtual void restartMediaSender() = 0;
virtual void restartMediaReceiver() = 0;
using StateListener = std::function<void(CallState, int)>;
template<class T>
void addStateListener(T&& list) {
stateChangedListeners_.emplace_back(std::forward<T>(list));
}
void addSubCall(const std::shared_ptr<Call>& call);
virtual void merge(std::shared_ptr<Call> scall);
protected:
/**
* Constructor of a call
......@@ -345,13 +354,18 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> {
bool isAudioMuted_{false};
bool isVideoMuted_{false};
bool quiet {false};
std::set<std::shared_ptr<Call>> subcalls {};
private:
bool validStateTransition(CallState newState);
/** Protect every attribute that can be changed by two threads */
mutable std::recursive_mutex callMutex_ {};
std::vector<std::function<void(CallState, ConnectionState, int)>> stateChangedListeners_ {};
// Informations about call socket / audio
/** My IP address */
......@@ -394,5 +408,3 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> {
};
} // namespace ring
#endif // __CALL_H__
......@@ -276,6 +276,16 @@ getMessageStatus(uint64_t id)
return ring::Manager::instance().getMessageStatus(id);