Commit 1d41f503 authored by Mohamed Chibani's avatar Mohamed Chibani
Browse files

media-attribute: set the security flag in media attributes

Set the security (SRTP) flag for media using account settings

Gitlab #556

Change-Id: I657c054625c37e3a3baa576efe27a67ea16bcff6
parent 1d798e05
......@@ -162,7 +162,7 @@ public:
* @return The created call
*/
virtual std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
const std::vector<MediaAttribute>& mediaList)
const std::vector<DRing::MediaMap>& mediaList)
= 0;
/**
......
......@@ -216,7 +216,7 @@ public:
* determine the response sent to the peer and the configuration
* of the local media.
*/
virtual void answer(const std::vector<MediaAttribute>& mediaList) = 0;
virtual void answer(const std::vector<DRing::MediaMap>& mediaList) = 0;
/**
* Answer to a media update request. The media attributes set by the
......@@ -227,7 +227,7 @@ public:
* call continue with the current media. It's up to the implementation
* to determine wether an answer will be sent to the peer.
*/
virtual void answerMediaChangeRequest(const std::vector<MediaAttribute>& mediaList) = 0;
virtual void answerMediaChangeRequest(const std::vector<DRing::MediaMap>& mediaList) = 0;
/**
* Hang up the call
* @param reason
......@@ -343,7 +343,7 @@ public: // media management
* @param mediaList the new media list
* @return true on success
*/
virtual bool requestMediaChange(const std::vector<MediaAttribute>& mediaList) = 0;
virtual bool requestMediaChange(const std::vector<DRing::MediaMap>& mediaList) = 0;
/**
* Send a message to a call identified by its callid
......
......@@ -59,7 +59,7 @@ CallFactory::newSipCall(const std::shared_ptr<SIPAccountBase>& account,
std::shared_ptr<SIPCall>
CallFactory::newSipCall(const std::shared_ptr<SIPAccountBase>& account,
Call::CallType type,
const std::vector<MediaAttribute>& mediaList)
const std::vector<DRing::MediaMap>& mediaList)
{
if (not allowNewCall_) {
JAMI_WARN("Creation of new calls is not allowed");
......
......@@ -64,7 +64,7 @@ public:
*/
std::shared_ptr<SIPCall> newSipCall(const std::shared_ptr<SIPAccountBase>& account,
Call::CallType type,
const std::vector<MediaAttribute>& mediaList);
const std::vector<DRing::MediaMap>& mediaList);
/**
* Forbid creation of new calls.
......
......@@ -391,38 +391,7 @@ JamiAccount::flush()
std::shared_ptr<SIPCall>
JamiAccount::newIncomingCall(const std::string& from,
const std::map<std::string, std::string>& details,
const std::shared_ptr<SipTransport>& sipTr)
{
if (sipTr) {
std::unique_lock<std::mutex> lk(sipConnsMtx_);
for (auto& [key, value] : sipConns_) {
if (key.first == from) {
// For each SipConnection of the device
for (auto cit = value.rbegin(); cit != value.rend(); ++cit) {
// Search linked Sip Transport
if (cit->transport != sipTr)
continue;
auto call = Manager::instance().callFactory.newSipCall(shared(),
Call::CallType::INCOMING);
call->setPeerUri(JAMI_URI_PREFIX + from);
call->setPeerNumber(from);
call->updateDetails(details);
return call;
}
}
}
lk.unlock();
}
JAMI_ERR("newIncomingCall: can't find matching call for %s", from.c_str());
return nullptr;
}
std::shared_ptr<SIPCall>
JamiAccount::newIncomingCall(const std::string& from,
const std::vector<MediaAttribute>& mediaList,
const std::vector<DRing::MediaMap>& mediaList,
const std::shared_ptr<SipTransport>& sipTransp)
{
JAMI_DBG("New incoming call from %s with %lu media", from.c_str(), mediaList.size());
......@@ -468,15 +437,14 @@ JamiAccount::newOutgoingCall(std::string_view toUrl,
}
std::shared_ptr<Call>
JamiAccount::newOutgoingCall(std::string_view toUrl,
const std::vector<MediaAttribute>& mediaAttrList)
JamiAccount::newOutgoingCall(std::string_view toUrl, const std::vector<DRing::MediaMap>& mediaList)
{
auto suffix = stripPrefix(toUrl);
JAMI_DBG() << *this << "Calling peer " << suffix;
auto& manager = Manager::instance();
auto call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaAttrList);
auto call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaList);
if (not call)
return {};
......@@ -492,8 +460,6 @@ JamiAccount::newOutgoingCallHelper(const std::shared_ptr<SIPCall>& call, std::st
auto suffix = stripPrefix(toUri);
JAMI_DBG() << *this << "Calling DHT peer " << suffix;
call->setSecure(isTlsEnabled());
try {
const std::string uri {parseJamiUri(suffix)};
startOutgoingCall(call, uri);
......@@ -531,12 +497,12 @@ JamiAccount::newOutgoingCallHelper(const std::shared_ptr<SIPCall>& call, std::st
std::shared_ptr<SIPCall>
JamiAccount::createSubCall(const std::shared_ptr<SIPCall>& mainCall)
{
auto mediaAttrList = mainCall->getMediaAttributeList();
auto mediaList = MediaAttribute::mediaAttributesToMediaMaps(mainCall->getMediaAttributeList());
if (not mediaAttrList.empty()) {
if (not mediaList.empty()) {
return Manager::instance().callFactory.newSipCall(shared(),
Call::CallType::OUTGOING,
mediaAttrList);
mediaList);
} else {
return Manager::instance().callFactory.newSipCall(shared(),
Call::CallType::OUTGOING,
......@@ -607,7 +573,6 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
// cached connection is failing with ICE (close event still not detected).
auto dummyCall = createSubCall(call);
dummyCall->setSecure(isTlsEnabled());
call->addSubCall(*dummyCall);
auto sendRequest =
[this, wCall, toUri, dummyCall = std::move(dummyCall)](const DeviceId& deviceId,
......@@ -628,7 +593,6 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
auto dev_call = createSubCall(call);
dev_call->setIPToIP(true);
dev_call->setSecure(isTlsEnabled());
dev_call->setState(Call::ConnectionState::TRYING);
call->addStateListener(
[w = weak(), deviceId](Call::CallState, Call::ConnectionState state, int) {
......@@ -674,7 +638,6 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
auto dev_call = createSubCall(call);
dev_call->setSecure(isTlsEnabled());
dev_call->setTransport(transport);
call->addSubCall(*dev_call);
// Set the call in PROGRESSING State because the ICE session
......
......@@ -266,19 +266,7 @@ public:
* @return A shared pointer on the created call.
*/
std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
const std::vector<MediaAttribute>& mediaList) override;
/**
* Create incoming SIPCall.
* @param[in] from The origin of the call
* @param details Call details
* @param sipTr: SIP Transport
* @return A shared pointer on the created call.
*/
std::shared_ptr<SIPCall> newIncomingCall(
const std::string& from,
const std::map<std::string, std::string>& details = {},
const std::shared_ptr<SipTransport>& sipTr = nullptr) override;
const std::vector<DRing::MediaMap>& mediaList) override;
/**
* Create incoming SIPCall.
......@@ -289,7 +277,7 @@ public:
*/
std::shared_ptr<SIPCall> newIncomingCall(
const std::string& from,
const std::vector<MediaAttribute>& mediaList,
const std::vector<DRing::MediaMap>& mediaList,
const std::shared_ptr<SipTransport>& sipTr = {}) override;
void onTextMessage(const std::string& id,
......@@ -533,7 +521,8 @@ public:
std::vector<uint8_t> conversationVCard(const std::string& conversationId) const;
// Member management
void saveMembers(const std::string& convId, const std::vector<std::string>& members); // Save confInfos
void saveMembers(const std::string& convId,
const std::vector<std::string>& members); // Save confInfos
void addConversationMember(const std::string& conversationId,
const std::string& contactUri,
bool sendRequest = true);
......
......@@ -1078,9 +1078,7 @@ Manager::requestMediaChange(const std::string& callID, const std::vector<DRing::
return false;
}
auto const& mediaAttrList = MediaAttribute::parseMediaList(mediaList);
return call->requestMediaChange(mediaAttrList);
return call->requestMediaChange(mediaList);
}
// THREAD=Main : for outgoing Call
......@@ -1159,8 +1157,7 @@ Manager::answerCallWithMedia(const std::string& callId,
stopTone();
try {
auto mediaAttrList = MediaAttribute::parseMediaList(mediaList);
call->answer(mediaAttrList);
call->answer(mediaList);
} catch (const std::runtime_error& e) {
JAMI_ERR("%s", e.what());
result = false;
......@@ -1205,8 +1202,7 @@ Manager::answerMediaChangeRequest(const std::string& callId,
}
try {
auto mediaAttrList = MediaAttribute::parseMediaList(mediaList);
call->answerMediaChangeRequest(mediaAttrList);
call->answerMediaChangeRequest(mediaList);
} catch (const std::runtime_error& e) {
JAMI_ERR("%s", e.what());
result = false;
......@@ -3379,7 +3375,7 @@ Manager::newOutgoingCall(std::string_view toUrl,
return {};
}
return account->newOutgoingCall(toUrl, MediaAttribute::parseMediaList(mediaList));
return account->newOutgoingCall(toUrl, mediaList);
}
#ifdef ENABLE_VIDEO
......
......@@ -23,7 +23,7 @@
namespace jami {
MediaAttribute::MediaAttribute(const DRing::MediaMap& mediaMap)
MediaAttribute::MediaAttribute(const DRing::MediaMap& mediaMap, bool secure)
{
std::pair<bool, MediaType> pairType = getMediaType(mediaMap);
if (pairType.first)
......@@ -51,16 +51,18 @@ MediaAttribute::MediaAttribute(const DRing::MediaMap& mediaMap)
pairBool = getBoolValue(mediaMap, DRing::Media::MediaAttributeKey::ON_HOLD);
if (pairBool.first)
onHold_ = pairBool.second;
secure_ = secure;
}
std::vector<MediaAttribute>
MediaAttribute::parseMediaList(const std::vector<DRing::MediaMap>& mediaList)
MediaAttribute::buildMediaAtrributesList(const std::vector<DRing::MediaMap>& mediaList, bool secure)
{
std::vector<MediaAttribute> mediaAttrList;
mediaAttrList.reserve(mediaList.size());
for (auto const& mediaMap : mediaList) {
mediaAttrList.emplace_back(MediaAttribute(mediaMap));
mediaAttrList.emplace_back(MediaAttribute(mediaMap, secure));
}
return mediaAttrList;
......
......@@ -47,9 +47,10 @@ public:
, onHold_(onHold)
{}
MediaAttribute(const DRing::MediaMap& mediaMap);
MediaAttribute(const DRing::MediaMap& mediaMap, bool secure);
static std::vector<MediaAttribute> parseMediaList(const std::vector<DRing::MediaMap>& mediaList);
static std::vector<MediaAttribute> buildMediaAtrributesList(
const std::vector<DRing::MediaMap>& mediaList, bool secure);
static MediaType stringToMediaType(const std::string& mediaType);
......
......@@ -989,6 +989,7 @@ Sdp::clearIce(pjmedia_sdp_session* session)
return;
pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "ice-ufrag");
pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "ice-pwd");
// TODO. Why this? we should not have "candidate" attribute at session level.
pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "candidate");
for (unsigned i = 0; i < session->media_count; i++) {
auto media = session->media[i];
......
......@@ -173,21 +173,10 @@ SIPAccount::~SIPAccount() noexcept
std::shared_ptr<SIPCall>
SIPAccount::newIncomingCall(const std::string& from UNUSED,
const std::map<std::string, std::string>& details,
const std::vector<DRing::MediaMap>& mediaList,
const std::shared_ptr<SipTransport>&)
{
auto& manager = Manager::instance();
return manager.callFactory.newSipCall(shared(), Call::CallType::INCOMING, details);
}
std::shared_ptr<SIPCall>
SIPAccount::newIncomingCall(const std::string& from UNUSED,
const std::vector<MediaAttribute>& mediaAttrList,
const std::shared_ptr<SipTransport>&)
{
return Manager::instance().callFactory.newSipCall(shared(),
Call::CallType::INCOMING,
mediaAttrList);
return Manager::instance().callFactory.newSipCall(shared(), Call::CallType::INCOMING, mediaList);
}
template<>
......@@ -204,8 +193,6 @@ SIPAccount::newOutgoingCall(std::string_view toUrl,
auto call = manager.callFactory.newSipCall(shared(),
Call::CallType::OUTGOING,
volatileCallDetails);
call->setSecure(isTlsEnabled());
if (isIP2IP()) {
bool ipv6 = IpAddr::isIpv6(toUrl);
to = ipv6 ? IpAddr(toUrl).toString(false, true) : toUrl;
......@@ -282,7 +269,7 @@ SIPAccount::newOutgoingCall(std::string_view toUrl,
}
std::shared_ptr<Call>
SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<MediaAttribute>& mediaAttrList)
SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<DRing::MediaMap>& mediaList)
{
std::string to;
int family;
......@@ -291,12 +278,9 @@ SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<MediaAttri
auto& manager = Manager::instance();
auto call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaAttrList);
auto call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaList);
if (not call)
throw std::runtime_error("Failed to create the call");
;
call->setSecure(isTlsEnabled());
if (isIP2IP()) {
bool ipv6 = IpAddr::isIpv6(toUrl);
......@@ -326,7 +310,7 @@ SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<MediaAttri
// Do not init ICE yet if the the media list is empty. This may occur
// if we are sending an invite with no SDP offer.
if (call->isIceEnabled() and not mediaAttrList.empty()) {
if (call->isIceEnabled() and not mediaList.empty()) {
call->initIceMediaTransport(true);
}
......@@ -356,7 +340,9 @@ SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<MediaAttri
else
sdp.setPublishedIP(getPublishedAddress());
const bool created = sdp.createOffer(mediaAttrList);
// TODO. We should not dot his here. Move it to SIPCall.
const bool created = sdp.createOffer(
MediaAttribute::buildMediaAtrributesList(mediaList, isSrtpEnabled()));
if (created) {
std::weak_ptr<SIPCall> weak_call = call;
......
......@@ -437,19 +437,7 @@ public:
* @return a shared pointer on the created call.
*/
std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
const std::vector<MediaAttribute>& mediaList) override;
/**
* Create incoming SIPCall.
* @param[in] from The origin uri of the call
* @param details use to set some specific details
* @return std::shared_ptr<T> A shared pointer on the created call.
* The type of this instance is given in template argument.
* This type can be any base class of SIPCall class (included).
*/
std::shared_ptr<SIPCall> newIncomingCall(const std::string& from,
const std::map<std::string, std::string>& details = {},
const std::shared_ptr<SipTransport>& = nullptr) override;
const std::vector<DRing::MediaMap>& mediaList) override;
/**
* Create incoming SIPCall.
......@@ -460,7 +448,7 @@ public:
*/
std::shared_ptr<SIPCall> newIncomingCall(
const std::string& from,
const std::vector<MediaAttribute>& mediaList,
const std::vector<DRing::MediaMap>& mediaList,
const std::shared_ptr<SipTransport>& sipTr = {}) override;
void onRegister(pjsip_regc_cbparam* param);
......
......@@ -133,20 +133,6 @@ public:
virtual ~SIPAccountBase() noexcept;
/**
* Create incoming SIPCall.
* @param[in] id The ID of the call
* @param details use to set some specific details
* @return std::shared_ptr<T> A shared pointer on the created call.
* The type of this instance is given in template argument.
* This type can be any base class of SIPCall class (included).
*/
virtual std::shared_ptr<SIPCall> newIncomingCall(
const std::string& from,
const std::map<std::string, std::string>& details = {},
const std::shared_ptr<SipTransport>& = nullptr)
= 0;
/**
* Create incoming SIPCall.
* @param[in] from The origin of the call
......@@ -155,7 +141,7 @@ public:
* @return A shared pointer on the created call.
*/
virtual std::shared_ptr<SIPCall> newIncomingCall(const std::string& from,
const std::vector<MediaAttribute>& mediaList,
const std::vector<DRing::MediaMap>& mediaList,
const std::shared_ptr<SipTransport>& sipTr = {})
= 0;
......
......@@ -91,6 +91,7 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
: Call(account, callId, type, details)
, sdp_(new Sdp(callId))
, enableIce_(account->isIceForMediaEnabled())
, srtpEnabled_(account->isSrtpEnabled())
{
if (account->getUPnPActive())
upnp_.reset(new upnp::Controller());
......@@ -120,11 +121,12 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
const std::string& callId,
Call::CallType type,
const std::vector<MediaAttribute>& mediaAttrList)
const std::vector<DRing::MediaMap>& mediaList)
: Call(account, callId, type)
, peerSupportMultiStream_(false)
, sdp_(new Sdp(callId))
, enableIce_(account->isIceForMediaEnabled())
, srtpEnabled_(account->isSrtpEnabled())
{
if (account->getUPnPActive())
upnp_.reset(new upnp::Controller());
......@@ -139,18 +141,20 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
account->getActiveAccountCodecInfoList(MEDIA_VIDEO));
#endif
std::vector<MediaAttribute> mediaList;
if (mediaAttrList.size() > 0) {
mediaList = mediaAttrList;
} else if (type_ == Call::CallType::INCOMING) {
// Handle incoming call without media offer.
JAMI_WARN("[call:%s] No media offered in the incoming invite. An offer will be provided in "
"the answer",
getCallId().c_str());
mediaList = getSIPAccount()->createDefaultMediaList(getSIPAccount()->isVideoEnabled(),
getState() == CallState::HOLD);
} else {
JAMI_WARN("[call:%s] Creating an outgoing call with empty offer", getCallId().c_str());
auto mediaAttrList = MediaAttribute::buildMediaAtrributesList(mediaList, isSrtpEnabled());
if (mediaAttrList.size() == 0) {
if (type_ == Call::CallType::INCOMING) {
// Handle incoming call without media offer.
JAMI_WARN(
"[call:%s] No media offered in the incoming invite. An offer will be provided in "
"the answer",
getCallId().c_str());
mediaAttrList = getSIPAccount()->createDefaultMediaList(getSIPAccount()->isVideoEnabled(),
getState() == CallState::HOLD);
} else {
JAMI_WARN("[call:%s] Creating an outgoing call with empty offer", getCallId().c_str());
}
}
JAMI_DBG("[call:%s] Create a new [%s] SIP call with %lu media",
......@@ -160,7 +164,7 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
: (type == Call::CallType::OUTGOING ? "OUTGOING" : "MISSED"),
mediaList.size());
initMediaStreams(mediaList);
initMediaStreams(mediaAttrList);
}
SIPCall::~SIPCall()
......@@ -422,37 +426,39 @@ SIPCall::setContactHeader(pj_str_t* contact)
void
SIPCall::setTransport(const std::shared_ptr<SipTransport>& t)
{
if (isSecure() and t and not t->isSecure()) {
JAMI_ERR("Can't set un-secure transport to secure call.");
transport_ = t;
if (not transport_) {
JAMI_WARN("[call:%s] Transport was set to null", getCallId().c_str());
return;
}
const auto list_id = reinterpret_cast<uintptr_t>(this);
if (transport_)
transport_->removeStateListener(list_id);
transport_ = t;
if (isSrtpEnabled() and not transport_->isSecure()) {
JAMI_WARN("[call:%s] Crypto (SRTP) is negotiated over an un-encrypted signaling channel",
getCallId().c_str());
}
if (transport_) {
setSecure(transport_->isSecure());
// listen for transport destruction
transport_->addStateListener(
list_id,
[wthis_ = weak()](pjsip_transport_state state, const pjsip_transport_state_info*) {
if (auto this_ = wthis_.lock()) {
// end the call if the SIP transport is shut down
auto isAlive = SipTransport::isAlive(state);
if (not isAlive
and this_->getConnectionState() != ConnectionState::DISCONNECTED) {
JAMI_WARN(
"[call:%s] Ending call because underlying SIP transport was closed",
this_->getCallId().c_str());
this_->stopAllMedia();
this_->onFailure(ECONNRESET);
}
}
});
if (not isSrtpEnabled() and transport_->isSecure()) {
JAMI_WARN("[call:%s] The signaling channel is encrypted but the media is not encrypted",
getCallId().c_str());
}
const auto list_id = reinterpret_cast<uintptr_t>(this);
transport_->removeStateListener(list_id);
// listen for transport destruction
transport_->addStateListener(
list_id, [wthis_ = weak()](pjsip_transport_state state, const pjsip_transport_state_info*) {
if (auto this_ = wthis_.lock()) {
// end the call if the SIP transport is shut down
auto isAlive = SipTransport::isAlive(state);
if (not isAlive and this_->getConnectionState() != ConnectionState::DISCONNECTED) {
JAMI_WARN("[call:%s] Ending call because underlying SIP transport was closed",
this_->getCallId().c_str());
this_->stopAllMedia();
this_->onFailure(ECONNRESET);
}
}
});
}
void
......@@ -760,7 +766,7 @@ SIPCall::answer()
}
void
SIPCall::answer(const std::vector<MediaAttribute>& mediaAttrList)
SIPCall::answer(const std::vector<DRing::MediaMap>& mediaList)
{
std::lock_guard<std::recursive_mutex> lk {callMutex_};
auto account = getSIPAccount();
......@@ -769,6 +775,8 @@ SIPCall::answer(const std::vector<MediaAttribute>& mediaAttrList)
return;
}
auto mediaAttrList = MediaAttribute::buildMediaAtrributesList(mediaList, isSrtpEnabled());
if (mediaAttrList.empty()) {
JAMI_DBG("[call:%s] Media list must not be empty!", getCallId().c_str());
return;
......@@ -894,7 +902,7 @@ SIPCall::answer(const std::vector<MediaAttribute>& mediaAttrList)
}
void
SIPCall::answerMediaChangeRequest(const std::vector<MediaAttribute>& mediaAttrList)
SIPCall::answerMediaChangeRequest(const std::vector<DRing::MediaMap>& mediaList)
{
std::lock_guard<std::recursive_mutex> lk {callMutex_};
......@@ -904,6 +912,8 @@ SIPCall::answerMediaChangeRequest(const std::vector<MediaAttribute>& mediaAttrLi
return;
}
auto mediaAttrList = MediaAttribute::buildMediaAtrributesList(mediaList, isSrtpEnabled());
if (mediaAttrList.empty()) {
JAMI_DBG("[call:%s] Media list size is empty. Ignoring the media change request",
getCallId().c_str());
......@@ -1879,7 +1889,7 @@ SIPCall::updateNegotiatedMedia()
continue;
}
if (isSecure() and local.enabled and not local.crypto) {
if (isSrtpEnabled() and local.enabled and not local.crypto) {
JAMI_WARN("[call:%s] [SDP:slot#%u] Secure mode but no local crypto attributes. "
"Ignoring the media",
getCallId().c_str(),
......@@ -1887,7 +1897,7 @@ SIPCall::updateNegotiatedMedia()
continue;
}
if (isSecure() and remote.enabled and not remote.crypto) {
if (isSrtpEnabled() and remote.enabled and not remote.crypto) {
JAMI_WARN("[call:%s] [SDP:slot#%u] Secure mode but no crypto remote attributes. "
"Ignoring the