Commit 50150c7f authored by Sylvain Berfini's avatar Sylvain Berfini 🎩
Browse files

Feature/multipart body invite master

parent 2724e7bc
......@@ -504,6 +504,21 @@ LINPHONE_PUBLIC void linphone_call_params_clear_custom_sdp_attributes(LinphoneCa
**/
LINPHONE_PUBLIC void linphone_call_params_clear_custom_sdp_media_attributes(LinphoneCallParams *params, LinphoneStreamType type);
/**
* Gets a list of #LinphoneContent set if exists
* @param[in] params The #LinphoneCallParams to get the custom Content from.
* @return \bctbx_list{LinphoneContent} A list of #LinphoneContent set if exists, NULL otherwise.
* @ingroup media_parameters
**/
LINPHONE_PUBLIC bctbx_list_t* linphone_call_params_get_custom_contents (const LinphoneCallParams *params);
/**
* Adds a #LinphoneContent to be added to the INVITE SDP.
* @param[in] params The #LinphoneCallParams in which to set the custom #LinphoneContent.
* @param[in] content The #LinphoneContent to be added.
* @ingroup media_parameters
**/
LINPHONE_PUBLIC void linphone_call_params_add_custom_content (LinphoneCallParams *params, LinphoneContent *content);
/*******************************************************************************
* DEPRECATED *
......
......@@ -493,6 +493,21 @@ void linphone_call_params_set_no_user_consent (LinphoneCallParams *params, bool_
L_GET_PRIVATE_FROM_C_OBJECT(params)->setNoUserConsent(!!value);
}
bctbx_list_t* linphone_call_params_get_custom_contents (const LinphoneCallParams *params) {
const list<LinphonePrivate::Content>& contents = L_GET_CPP_PTR_FROM_C_OBJECT(params)->getCustomContents();
bctbx_list_t* c_contents = nullptr;
for (auto& content : contents) {
LinphoneContent *c_content = L_GET_C_BACK_PTR(&content);
c_contents = bctbx_list_append(c_contents, linphone_content_ref(c_content));
}
return c_contents;
}
void linphone_call_params_add_custom_content (LinphoneCallParams *params, LinphoneContent *content) {
LinphonePrivate::Content *cppContent = L_GET_CPP_PTR_FROM_C_OBJECT(content);
L_GET_CPP_PTR_FROM_C_OBJECT(params)->addCustomContent(*cppContent);
}
// =============================================================================
// Reference and user data handling functions.
// =============================================================================
......
......@@ -394,7 +394,7 @@ void *linphone_chat_message_get_message_state_changed_cb_user_data (LinphoneChat
// =============================================================================
const char *linphone_chat_message_get_content_type (const LinphoneChatMessage *msg) {
msg->cache.contentType = L_GET_PRIVATE_FROM_C_OBJECT(msg)->getContentType().asString();
msg->cache.contentType = L_GET_PRIVATE_FROM_C_OBJECT(msg)->getContentType().getMediaType();
return L_STRING_TO_C(msg->cache.contentType);
}
......
......@@ -682,11 +682,12 @@ LinphoneReason ChatMessagePrivate::receive () {
for (Content *c : contents) {
ContentType ct(c->getContentType());
ct.cleanParameters();
if (linphone_core_is_content_type_supported(core->getCCore(), ct.asString().c_str())) {
string contenttype = ct.getType() + "/" + ct.getSubType();
if (linphone_core_is_content_type_supported(core->getCCore(), contenttype.c_str())) {
foundSupportContentType = true;
break;
} else
lError() << "Unsupported content-type: " << c->getContentType();
lError() << "Unsupported content-type: " << contenttype;
}
if (!foundSupportContentType) {
......
......@@ -109,7 +109,7 @@ ChatMessageModifier::Result CpimChatMessageModifier::encode (const shared_ptr<Ch
);
}
cpimMessage.addContentHeader(
Cpim::GenericHeader("Content-Type", content->getContentType().asString())
Cpim::GenericHeader("Content-Type", content->getContentType().getMediaType())
);
cpimMessage.addContentHeader(
Cpim::GenericHeader("Content-Length", Utils::toString(contentBody.size()))
......
......@@ -63,6 +63,7 @@ private:
SalCustomHeader *customHeaders = nullptr;
std::unordered_map<std::string, std::string> customContactParameters;
std::shared_ptr<CallSession> referer; /* In case call creation is consecutive to an incoming transfer, this points to the original call */
std::list<Content> customContents;
L_DECLARE_PUBLIC(CallSessionParams);
};
......
......@@ -42,6 +42,7 @@ void CallSessionParamsPrivate::clone (const CallSessionParamsPrivate *src) {
customHeaders = sal_custom_header_clone(src->customHeaders);
customContactParameters = src->customContactParameters;
referer = src->referer;
customContents = src->customContents;
}
// -----------------------------------------------------------------------------
......@@ -157,4 +158,16 @@ std::string CallSessionParams::getCustomContactParameter (const std::string &par
return it->second;
}
// -----------------------------------------------------------------------------
void CallSessionParams::addCustomContent(const Content& content) {
L_D();
d->customContents.push_back(move(content));
}
const list<Content>& CallSessionParams::getCustomContents() const {
L_D();
return d->customContents;
}
LINPHONE_END_NAMESPACE
......@@ -23,6 +23,7 @@
#include "object/clonable-object.h"
#include "linphone/types.h"
#include "content/content.h"
#include "c-wrapper/internal/c-sal.h"
#include "sal/sal.h"
......@@ -65,6 +66,9 @@ public:
void clearCustomContactParameters ();
std::string getCustomContactParameter (const std::string &paramName) const;
void addCustomContent (const Content& content);
const std::list<Content>& getCustomContents () const;
protected:
explicit CallSessionParams (CallSessionParamsPrivate &p);
......
......@@ -1145,6 +1145,12 @@ int CallSession::startInvite (const Address *destination, const string &subject,
shared_ptr<CallSession> ref = getSharedFromThis();
if (content)
d->op->setLocalBody(*content);
// If a custom Content has been set in the call params, create a multipart body for the INVITE
for (auto& content : d->params->getCustomContents()) {
d->op->addAdditionalLocalBody(content);
}
int result = d->op->call(from, destinationStr, subject);
ms_free(from);
if (result < 0) {
......@@ -1328,6 +1334,11 @@ const CallSessionParams * CallSession::getRemoteParams () {
d->remoteParams = new CallSessionParams();
d->remoteParams->getPrivate()->setCustomHeaders(ch);
}
const list<Content> additionnalContents = d->op->getAdditionalRemoteBodies();
for (auto& content : additionnalContents)
d->remoteParams->addCustomContent(content);
return d->remoteParams;
}
return nullptr;
......
......@@ -5326,6 +5326,11 @@ const MediaSessionParams * MediaSession::getRemoteParams () {
d->setRemoteParams(new MediaSessionParams());
d->getRemoteParams()->getPrivate()->setCustomHeaders(ch);
}
const list<Content> additionnalContents = d->op->getAdditionalRemoteBodies();
for (auto& content : additionnalContents)
d->remoteParams->addCustomContent(content);
return d->getRemoteParams();
}
return nullptr;
......
......@@ -60,6 +60,7 @@ const ContentType ContentType::SipFrag("message/sipfrag");
ContentType::ContentType (const string &contentType) : Header(*new ContentTypePrivate) {
L_D();
setName("Content-Type");
size_t pos = contentType.find('/');
size_t posParam = contentType.find(";");
size_t end = contentType.length();
......@@ -92,6 +93,7 @@ ContentType::ContentType (const string &contentType) : Header(*new ContentTypePr
ContentType::ContentType (const string &type, const string &subType) : Header(*new ContentTypePrivate) {
L_D();
setName("Content-Type");
if (setType(type) && !setSubType(subType))
d->type.clear();
}
......@@ -103,6 +105,7 @@ ContentType::ContentType (
) : Header(*new ContentTypePrivate) {
L_D();
setName("Content-Type");
if (setType(type) && !setSubType(subType))
d->type.clear();
addParameter(parameter);
......@@ -115,6 +118,7 @@ ContentType::ContentType (
) : Header(*new ContentTypePrivate) {
L_D();
setName("Content-Type");
if (setType(type) && !setSubType(subType))
d->type.clear();
addParameters(parameters);
......@@ -124,6 +128,7 @@ ContentType::ContentType (const ContentType &other) : ContentType(other.getType(
ContentType &ContentType::operator= (const ContentType &other) {
if (this != &other) {
setName("Content-Type");
setType(other.getType());
setSubType(other.getSubType());
cleanParameters();
......@@ -219,4 +224,18 @@ bool ContentType::isFile (const ContentType &contentType) {
contentType != ConferenceInfo;
}
string ContentType::getMediaType () const {
stringstream asString;
asString << getValue();
for (const auto &param : getParameters())
asString << param.asString();
return asString.str();
}
ostream &operator<< (ostream &os, const ContentType& contentType) {
os << contentType.getMediaType();
return os;
}
LINPHONE_END_NAMESPACE
......@@ -64,6 +64,9 @@ public:
bool isMultipart() const;
static bool isFile (const ContentType &contentType);
std::string getMediaType() const;
LINPHONE_PUBLIC friend std::ostream &operator<< (std::ostream &os, const ContentType &header);
static const ContentType ConferenceInfo;
static const ContentType Cpim;
......
......@@ -181,6 +181,11 @@ bool Content::isEmpty () const {
return getSize() == 0;
}
bool Content::isMultipart () const {
L_D();
return d->contentType.isValid() && d->contentType == ContentType::Multipart;
}
bool Content::isValid () const {
L_D();
return d->contentType.isValid() || (d->contentType.isEmpty() && d->body.empty());
......
......@@ -77,7 +77,7 @@ public:
size_t getSize () const;
bool isValid () const;
bool isMultipart () const;
bool isEmpty () const;
virtual bool isFile () const;
......
......@@ -94,7 +94,7 @@ void Header::setName (const string &name) {
d->name = name;
}
string Header::getName () const {
const string& Header::getName () const {
L_D();
return d->name;
}
......@@ -104,7 +104,7 @@ void Header::setValue (const string &value) {
d->value = value;
}
string Header::getValue () const {
const string& Header::getValue () const {
L_D();
return d->value;
}
......
......@@ -48,10 +48,10 @@ public:
bool operator!= (const Header &other) const;
void setName (const std::string &name);
std::string getName () const;
const std::string& getName () const;
void setValue (const std::string &value);
std::string getValue () const;
const std::string& getValue () const;
std::string getValueWithParams () const;
void cleanParameters ();
......
......@@ -282,7 +282,7 @@ void MainDbPrivate::insertContent (long long chatMessageId, const Content &conte
#ifdef HAVE_DB_STORAGE
soci::session *session = dbSession.getBackendSession();
const long long &contentTypeId = insertContentType(content.getContentType().asString());
const long long &contentTypeId = insertContentType(content.getContentType().getMediaType());
const string &body = content.getBodyAsString();
*session << "INSERT INTO chat_message_content (event_id, content_type_id, body) VALUES"
" (:chatMessageId, :contentTypeId, :body)", soci::use(chatMessageId), soci::use(contentTypeId),
......@@ -1534,7 +1534,7 @@ static string extractLegacyFileContentType (const string &xml) {
for (xmlElement = xmlElement->xmlChildrenNode; xmlElement; xmlElement = xmlElement->next)
if (!xmlStrcmp(xmlElement->name, XmlCharPtr("content-type"))) {
XmlCharObject xmlContentType(xmlNodeListGetString(xmlMessageBody.get(), xmlElement->xmlChildrenNode, 1));
return ContentType(reinterpret_cast<const char *>(xmlContentType.get())).asString();
return ContentType(reinterpret_cast<const char *>(xmlContentType.get())).getMediaType();
}
}
......
......@@ -20,6 +20,7 @@
#include "bellesip_sal/sal_impl.h"
#include "offeranswer.h"
#include "sal/call-op.h"
#include "content/content-manager.h"
#include <bctoolbox/defs.h>
#include <belle-sip/provider.h>
......@@ -102,6 +103,14 @@ int SalCallOp::setLocalBody (Content &&body) {
return 0;
}
void SalCallOp::addAdditionalLocalBody (const Content &content) {
mAdditionalLocalBodies.push_back(move(content));
}
const list<Content>& SalCallOp::getAdditionalRemoteBodies () const {
return mAdditionalRemoteBodies;
}
belle_sip_header_allow_t *SalCallOp::createAllow (bool enableUpdate) {
ostringstream oss;
oss << "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO";
......@@ -166,7 +175,20 @@ void SalCallOp::fillInvite (belle_sip_request_t *invite) {
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite), belle_sip_header_create("Supported", "timer"));
}
mSdpOffering = (mLocalBody.getContentType() == ContentType::Sdp);
setCustomBody(BELLE_SIP_MESSAGE(invite), mLocalBody);
if (!mAdditionalLocalBodies.empty()) {
list<Content *> contents;
if (!mLocalBody.isEmpty()) {
contents.push_back(&mLocalBody);
}
for (auto& body : mAdditionalLocalBodies) {
contents.push_back(&body);
}
Content multipartContent = ContentManager::contentListToMultipart(contents);
setCustomBody(BELLE_SIP_MESSAGE(invite), multipartContent);
} else {
setCustomBody(BELLE_SIP_MESSAGE(invite), mLocalBody);
}
}
void SalCallOp::setReleased () {
......@@ -330,21 +352,34 @@ void SalCallOp::handleBodyFromResponse (belle_sip_response_t *response) {
sal_media_description_unref(mRemoteMedia);
mRemoteMedia = nullptr;
}
if (body.getContentType() == ContentType::Sdp) {
Content sdpBody = body;
if (body.isMultipart()) {
list<Content> contents = ContentManager::multipartToContentList(body);
for (auto& content : contents) {
if (content.getContentType() == ContentType::Sdp) {
sdpBody = content;
} else {
mAdditionalRemoteBodies.push_back(move(content));
}
}
}
if (sdpBody.getContentType() == ContentType::Sdp) {
belle_sdp_session_description_t *sdp = nullptr;
SalReason reason;
if (parseSdpBody(body, &sdp, &reason) == 0) {
if (parseSdpBody(sdpBody, &sdp, &reason) == 0) {
if (sdp) {
mRemoteMedia = sal_media_description_new();
sdp_to_media_description(sdp, mRemoteMedia);
mRemoteBody = move(body);
mRemoteBody = move(sdpBody);
} // If no SDP in response, what can we do?
}
// Process sdp in any case to reset result media description
if (mLocalMedia)
sdpProcess();
} else {
mRemoteBody = move(body);
mRemoteBody = move(sdpBody);
}
}
......@@ -582,9 +617,21 @@ SalReason SalCallOp::processBodyForInvite (belle_sip_request_t *invite) {
if (!body.isValid())
return SalReasonUnsupportedContent;
if ((body.getContentType() == ContentType::Sdp) || (body.getContentType().isEmpty() && body.isEmpty())) {
Content sdpBody = body;
if (body.isMultipart()) {
list<Content> contents = ContentManager::multipartToContentList(body);
for (auto& content : contents) {
if (content.getContentType() == ContentType::Sdp) {
sdpBody = content;
} else {
mAdditionalRemoteBodies.push_back(move(content));
}
}
}
if ((sdpBody.getContentType() == ContentType::Sdp) || (sdpBody.getContentType().isEmpty() && sdpBody.isEmpty())) {
belle_sdp_session_description_t *sdp;
if (parseSdpBody(body, &sdp, &reason) == 0) {
if (parseSdpBody(sdpBody, &sdp, &reason) == 0) {
if (sdp) {
mSdpOffering = false;
if (mRemoteMedia)
......@@ -607,7 +654,7 @@ SalReason SalCallOp::processBodyForInvite (belle_sip_request_t *invite) {
sal_error_info_reset(&sei);
}
}
mRemoteBody = move(body);
mRemoteBody = move(sdpBody);
return reason;
}
......@@ -616,9 +663,22 @@ SalReason SalCallOp::processBodyForAck (belle_sip_request_t *ack) {
Content body = extractBody(BELLE_SIP_MESSAGE(ack));
if (!body.isValid())
return SalReasonUnsupportedContent;
if (body.getContentType() == ContentType::Sdp) {
Content sdpBody = body;
if (body.isMultipart()) {
list<Content> contents = ContentManager::multipartToContentList(body);
for (auto& content : contents) {
if (content.getContentType() == ContentType::Sdp) {
sdpBody = content;
} else {
mAdditionalRemoteBodies.push_back(move(content));
}
}
}
if (sdpBody.getContentType() == ContentType::Sdp) {
belle_sdp_session_description_t *sdp;
if (parseSdpBody(body, &sdp, &reason) == 0) {
if (parseSdpBody(sdpBody, &sdp, &reason) == 0) {
if (sdp) {
if (mRemoteMedia)
sal_media_description_unref(mRemoteMedia);
......@@ -631,7 +691,7 @@ SalReason SalCallOp::processBodyForAck (belle_sip_request_t *ack) {
}
}
}
mRemoteBody = move(body);
mRemoteBody = move(sdpBody);
return reason;
}
......
......@@ -34,6 +34,8 @@ public:
int setLocalMediaDescription (SalMediaDescription *desc);
int setLocalBody (const Content &body);
int setLocalBody (Content &&body);
void addAdditionalLocalBody (const Content &content);
const std::list<Content>& getAdditionalRemoteBodies () const;
SalMediaDescription *getRemoteMediaDescription () { return mRemoteMedia; }
const Content &getRemoteBody () const { return mRemoteBody; }
......@@ -121,6 +123,8 @@ private:
SalMediaDescription *mRemoteMedia = nullptr;
Content mLocalBody;
Content mRemoteBody;
std::list<Content> mAdditionalLocalBodies;
std::list<Content> mAdditionalRemoteBodies;
};
LINPHONE_END_NAMESPACE
......
......@@ -49,7 +49,7 @@ protected:
belle_sip_header_create("Content-Encoding", contentEncoding.c_str())
);
const ContentType &contentType = content.getContentType();
std::string contentTypeStr = std::string(BELLE_SIP_CONTENT_TYPE ": ") + contentType.asString();
std::string contentTypeStr = contentType.asString();
belle_sip_message_add_header(
BELLE_SIP_MESSAGE(req),
BELLE_SIP_HEADER(belle_sip_header_content_type_parse(contentTypeStr.c_str()))
......
......@@ -22,6 +22,7 @@
#include "c-wrapper/internal/c-tools.h"
#include "bellesip_sal/sal_impl.h"
#include "sal/op.h"
#include "content/header/header-param.h"
using namespace std;
......@@ -958,7 +959,7 @@ int SalOp::setCustomBody(belle_sip_message_t *msg, const Content &body) {
}
if (contentType.isValid()) {
belle_sip_header_content_type_t *content_type = belle_sip_header_content_type_create(contentType.getType().c_str(), contentType.getSubType().c_str());
belle_sip_header_content_type_t *content_type = belle_sip_header_content_type_parse(contentType.asString().c_str());
belle_sip_message_add_header(msg, BELLE_SIP_HEADER(content_type));
}
if (contentDisposition.isValid()) {
......
......@@ -248,7 +248,7 @@ static void early_media_with_multicast_audio(void) {
early_media_with_multicast_base(FALSE);
}
static void unicast_incoming_with_multicast_audio_on(void) {
simple_call_base(TRUE, FALSE);
simple_call_base(TRUE, FALSE, FALSE);
}
#ifdef VIDEO_ENABLED
static void early_media_with_multicast_video(void) {
......
......@@ -154,7 +154,9 @@ void liblinphone_tester_check_rtcp(LinphoneCoreManager* caller, LinphoneCoreMana
linphone_call_unref(c2);
}
void simple_call_base(bool_t enable_multicast_recv_side, bool_t disable_soundcard) {
static const char *info_content = "<somexml>blabla</somexml>";
void simple_call_base(bool_t enable_multicast_recv_side, bool_t disable_soundcard, bool_t use_multipart_invite_body) {
LinphoneCoreManager* marie;
LinphoneCoreManager* pauline;
const LinphoneAddress *from;
......@@ -177,33 +179,68 @@ void simple_call_base(bool_t enable_multicast_recv_side, bool_t disable_soundcar
marie_tmp_id = linphone_address_as_string(marie_addr);
linphone_proxy_config_edit(marie_cfg);
linphone_proxy_config_set_identity(marie_cfg,marie_tmp_id);
linphone_proxy_config_set_identity(marie_cfg, marie_tmp_id);
linphone_proxy_config_done(marie_cfg);
ms_free(marie_tmp_id);
linphone_address_unref(marie_addr);
}
linphone_core_enable_audio_multicast(pauline->lc,enable_multicast_recv_side);
linphone_core_enable_audio_multicast(pauline->lc, enable_multicast_recv_side);
BC_ASSERT_TRUE(call(marie,pauline));
pauline_call=linphone_core_get_current_call(pauline->lc);
if (use_multipart_invite_body) {
LinphoneCallParams *params = linphone_core_create_call_params(marie->lc, NULL);
LinphoneContent *content = linphone_core_create_content(marie->lc);
linphone_content_set_type(content, "application");
linphone_content_set_subtype(content, "somexml");
linphone_content_set_buffer(content, (const uint8_t *)info_content, strlen(info_content));
linphone_call_params_add_custom_content(params, content);
BC_ASSERT_TRUE(call_with_caller_params(marie, pauline, params));
linphone_call_params_unref(params);
linphone_content_unref(content);
} else {
BC_ASSERT_TRUE(call(marie, pauline));
}
pauline_call = linphone_core_get_current_call(pauline->lc);
BC_ASSERT_PTR_NOT_NULL(pauline_call);
/*check that display name is correctly propagated in From */
if (pauline_call){
from=linphone_call_get_remote_address(linphone_core_get_current_call(pauline->lc));
if (pauline_call) {
from = linphone_call_get_remote_address(linphone_core_get_current_call(pauline->lc));
BC_ASSERT_PTR_NOT_NULL(from);
if (from){
const char *dname=linphone_address_get_display_name(from);
if (from) {
const char *dname = linphone_address_get_display_name(from);
BC_ASSERT_PTR_NOT_NULL(dname);
if (dname){
BC_ASSERT_STRING_EQUAL(dname, "Super Marie");
}
}
const LinphoneCallParams *params = linphone_call_get_remote_params(pauline_call);
bctbx_list_t *parts = linphone_call_params_get_custom_contents(params);
if (use_multipart_invite_body) {
BC_ASSERT_PTR_NOT_NULL(parts);
if (parts) {
BC_ASSERT_EQUAL(bctbx_list_size(parts), 1, int, "%i");
LinphoneContent *content = (LinphoneContent *)bctbx_list_get_data(parts);
BC_ASSERT_PTR_NOT_NULL(content);
if (content) {
BC_ASSERT_STRING_EQUAL(linphone_content_get_type(content), "application");
BC_ASSERT_STRING_EQUAL(linphone_content_get_subtype(content), "somexml");
BC_ASSERT_STRING_EQUAL(linphone_content_get_string_buffer(content), info_content);
}
bctbx_list_free_with_data(parts, (void (*)(void *)) linphone_content_unref);
}
} else {
BC_ASSERT_PTR_NULL(parts);
}
}
liblinphone_tester_check_rtcp(marie,pauline);
end_call(marie,pauline