forked from mirrors/linphone-iphone
Rework chat handling.
- Add content type information in LIME encrypted message - Single entry point for all types of chat messages that are first decrypted if necessary and then handled according to their content type - Add possibility to send chat messages with a content type that is not 'text/plain' - Encrypt IMDN
This commit is contained in:
parent
1c7fc21b3d
commit
b297a4cb1d
18 changed files with 631 additions and 393 deletions
|
|
@ -591,12 +591,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){
|
|||
ctx->callbacks.notify_presence=(SalOnNotifyPresence)unimplemented_stub;
|
||||
if (ctx->callbacks.subscribe_presence_received==NULL)
|
||||
ctx->callbacks.subscribe_presence_received=(SalOnSubscribePresenceReceived)unimplemented_stub;
|
||||
if (ctx->callbacks.text_received==NULL)
|
||||
ctx->callbacks.text_received=(SalOnTextReceived)unimplemented_stub;
|
||||
if (ctx->callbacks.is_composing_received==NULL)
|
||||
ctx->callbacks.is_composing_received=(SalOnIsComposingReceived)unimplemented_stub;
|
||||
if (ctx->callbacks.imdn_received == NULL)
|
||||
ctx->callbacks.imdn_received = (SalOnImdnReceived)unimplemented_stub;
|
||||
if (ctx->callbacks.message_received==NULL)
|
||||
ctx->callbacks.message_received=(SalOnMessageReceived)unimplemented_stub;
|
||||
if (ctx->callbacks.ping_reply==NULL)
|
||||
ctx->callbacks.ping_reply=(SalOnPingReply)unimplemented_stub;
|
||||
if (ctx->callbacks.auth_requested==NULL)
|
||||
|
|
@ -618,6 +614,7 @@ void sal_uninit(Sal* sal){
|
|||
belle_sip_object_unref(sal->listener);
|
||||
if (sal->supported) belle_sip_object_unref(sal->supported);
|
||||
bctbx_list_free_with_data(sal->supported_tags,ms_free);
|
||||
bctbx_list_free_with_data(sal->supported_content_types, ms_free);
|
||||
if (sal->uuid) ms_free(sal->uuid);
|
||||
if (sal->root_ca) ms_free(sal->root_ca);
|
||||
if (sal->root_ca_data) ms_free(sal->root_ca_data);
|
||||
|
|
@ -1500,4 +1497,17 @@ void *sal_get_stack_impl(Sal *sal) {
|
|||
return sal->stack;
|
||||
}
|
||||
|
||||
bool_t sal_is_content_type_supported(const Sal *sal, const char *content_type) {
|
||||
bctbx_list_t *item;
|
||||
for (item = sal->supported_content_types; item != NULL; item = bctbx_list_next(item)) {
|
||||
const char *item_content_type = (const char *)bctbx_list_get_data(item);
|
||||
if (strcmp(item_content_type, content_type) == 0) return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void sal_add_content_type_support(Sal *sal, const char *content_type) {
|
||||
if ((content_type != NULL) && (sal_is_content_type_supported(sal, content_type) == FALSE)) {
|
||||
sal->supported_content_types = bctbx_list_append(sal->supported_content_types, ms_strdup(content_type));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ struct Sal{
|
|||
SalOpSDPHandling default_sdp_handling;
|
||||
bool_t pending_trans_checking; /*testing purpose*/
|
||||
void *ssl_config;
|
||||
bctbx_list_t *supported_content_types; /* list of char* */
|
||||
};
|
||||
|
||||
typedef enum SalOpState {
|
||||
|
|
@ -178,4 +179,5 @@ void _sal_op_add_custom_headers(SalOp *op, belle_sip_message_t *msg);
|
|||
|
||||
SalSubscribeStatus belle_sip_message_get_subscription_state(const belle_sip_message_t *msg);
|
||||
|
||||
|
||||
#endif /* SAL_IMPL_H_ */
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
|
||||
static void process_error( SalOp* op) {
|
||||
if (op->dir == SalOpDirOutgoing) {
|
||||
op->base.root->callbacks.text_delivery_update(op, SalTextDeliveryFailed);
|
||||
op->base.root->callbacks.message_delivery_update(op, SalMessageDeliveryFailed);
|
||||
} else {
|
||||
ms_warning("unexpected io error for incoming message on op [%p]",op);
|
||||
}
|
||||
|
|
@ -46,35 +46,39 @@ static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *eve
|
|||
static void process_response_event(void *op_base, const belle_sip_response_event_t *event){
|
||||
SalOp* op = (SalOp*)op_base;
|
||||
int code = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event));
|
||||
SalTextDeliveryStatus status;
|
||||
SalMessageDeliveryStatus status;
|
||||
sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event));
|
||||
|
||||
if (code>=100 && code <200)
|
||||
status=SalTextDeliveryInProgress;
|
||||
status=SalMessageDeliveryInProgress;
|
||||
else if (code>=200 && code <300)
|
||||
status=SalTextDeliveryDone;
|
||||
status=SalMessageDeliveryDone;
|
||||
else
|
||||
status=SalTextDeliveryFailed;
|
||||
status=SalMessageDeliveryFailed;
|
||||
|
||||
op->base.root->callbacks.text_delivery_update(op,status);
|
||||
op->base.root->callbacks.message_delivery_update(op,status);
|
||||
}
|
||||
|
||||
static bool_t is_external_body(belle_sip_header_content_type_t* content_type) {
|
||||
return strcmp("message",belle_sip_header_content_type_get_type(content_type))==0
|
||||
&& strcmp("external-body",belle_sip_header_content_type_get_subtype(content_type))==0;
|
||||
}
|
||||
static bool_t is_im_iscomposing(belle_sip_header_content_type_t* content_type) {
|
||||
return strcmp("application",belle_sip_header_content_type_get_type(content_type))==0
|
||||
&& strcmp("im-iscomposing+xml",belle_sip_header_content_type_get_subtype(content_type))==0;
|
||||
}
|
||||
|
||||
static bool_t is_imdn_xml(belle_sip_header_content_type_t *content_type) {
|
||||
return (strcmp("message", belle_sip_header_content_type_get_type(content_type)) == 0)
|
||||
&& (strcmp("imdn+xml", belle_sip_header_content_type_get_subtype(content_type)) == 0);
|
||||
}
|
||||
static void add_message_accept(SalOp *op, belle_sip_message_t *msg) {
|
||||
bctbx_list_t *item;
|
||||
const char *str;
|
||||
char *old;
|
||||
char *header = ms_strdup("xml/cipher, application/cipher.vnd.gsma.rcs-ft-http+xml");
|
||||
|
||||
static void add_message_accept(belle_sip_message_t *msg){
|
||||
belle_sip_message_add_header(msg,belle_sip_header_create("Accept","text/plain, message/external-body, application/im-iscomposing+xml, xml/cipher, application/vnd.gsma.rcs-ft-http+xml, application/cipher.vnd.gsma.rcs-ft-http+xml, message/imdn+xml"));
|
||||
for (item = op->base.root->supported_content_types; item != NULL; item = bctbx_list_next(item)) {
|
||||
str = (const char *)bctbx_list_get_data(item);
|
||||
old = header;
|
||||
header = ms_strdup_printf("%s, %s", old, str);
|
||||
ms_free(old);
|
||||
}
|
||||
|
||||
belle_sip_message_add_header(msg, belle_sip_header_create("Accept", header));
|
||||
ms_free(header);
|
||||
}
|
||||
|
||||
void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *event){
|
||||
|
|
@ -95,72 +99,45 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
|
|||
content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t);
|
||||
|
||||
if (content_type) {
|
||||
|
||||
SalMessage salmsg;
|
||||
char message_id[256]={0};
|
||||
|
||||
if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
|
||||
op->pending_server_trans=server_transaction;
|
||||
belle_sip_object_ref(op->pending_server_trans);
|
||||
|
||||
if (is_im_iscomposing(content_type)) {
|
||||
SalIsComposing saliscomposing;
|
||||
address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
|
||||
,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
|
||||
from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
|
||||
saliscomposing.from=from;
|
||||
saliscomposing.text=belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
|
||||
op->base.root->callbacks.is_composing_received(op,&saliscomposing);
|
||||
belle_sip_object_unref(address);
|
||||
belle_sip_free(from);
|
||||
} else if (is_imdn_xml(content_type)) {
|
||||
SalImdn salimdn;
|
||||
address = belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header)),
|
||||
belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
|
||||
from = belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
|
||||
salimdn.from = from;
|
||||
salimdn.content = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
|
||||
op->base.root->callbacks.imdn_received(op, &salimdn);
|
||||
belle_sip_object_unref(address);
|
||||
belle_sip_free(from);
|
||||
} else {
|
||||
SalMessage salmsg;
|
||||
char message_id[256]={0};
|
||||
|
||||
external_body=is_external_body(content_type);
|
||||
|
||||
address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
|
||||
,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
|
||||
from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
|
||||
snprintf(message_id,sizeof(message_id)-1,"%s%i"
|
||||
,belle_sip_header_call_id_get_call_id(call_id)
|
||||
,belle_sip_header_cseq_get_seq_number(cseq));
|
||||
salmsg.from=from;
|
||||
/* if we just deciphered a message, use the deciphered part(which can be a rcs xml body pointing to the file to retreive from server)*/
|
||||
salmsg.text=(!external_body)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
|
||||
salmsg.url=NULL;
|
||||
salmsg.content_type = ms_strdup_printf("%s/%s", belle_sip_header_content_type_get_type(content_type), belle_sip_header_content_type_get_subtype(content_type));
|
||||
if (external_body && belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")) {
|
||||
size_t url_length=strlen(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL"));
|
||||
salmsg.url = ms_strdup(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")+1); /* skip first "*/
|
||||
((char*)salmsg.url)[url_length-2]='\0'; /*remove trailing "*/
|
||||
}
|
||||
salmsg.message_id=message_id;
|
||||
salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL);
|
||||
op->base.root->callbacks.text_received(op,&salmsg);
|
||||
|
||||
belle_sip_object_unref(address);
|
||||
belle_sip_free(from);
|
||||
if (salmsg.url) ms_free((char*)salmsg.url);
|
||||
ms_free((char *)salmsg.content_type);
|
||||
external_body=is_external_body(content_type);
|
||||
address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
|
||||
,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
|
||||
from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
|
||||
snprintf(message_id,sizeof(message_id)-1,"%s%i"
|
||||
,belle_sip_header_call_id_get_call_id(call_id)
|
||||
,belle_sip_header_cseq_get_seq_number(cseq));
|
||||
salmsg.from=from;
|
||||
/* if we just deciphered a message, use the deciphered part(which can be a rcs xml body pointing to the file to retreive from server)*/
|
||||
salmsg.text=(!external_body)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
|
||||
salmsg.url=NULL;
|
||||
salmsg.content_type = ms_strdup_printf("%s/%s", belle_sip_header_content_type_get_type(content_type), belle_sip_header_content_type_get_subtype(content_type));
|
||||
if (external_body && belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")) {
|
||||
size_t url_length=strlen(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL"));
|
||||
salmsg.url = ms_strdup(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")+1); /* skip first "*/
|
||||
((char*)salmsg.url)[url_length-2]='\0'; /*remove trailing "*/
|
||||
}
|
||||
salmsg.message_id=message_id;
|
||||
salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL);
|
||||
op->base.root->callbacks.message_received(op,&salmsg);
|
||||
|
||||
belle_sip_object_unref(address);
|
||||
belle_sip_free(from);
|
||||
if (salmsg.url) ms_free((char*)salmsg.url);
|
||||
ms_free((char *)salmsg.content_type);
|
||||
} else {
|
||||
ms_error("Unsupported MESSAGE (no Content-Type)");
|
||||
goto error;
|
||||
resp = belle_sip_response_create_from_request(req, errcode);
|
||||
add_message_accept(op, (belle_sip_message_t*)resp);
|
||||
belle_sip_server_transaction_send_response(server_transaction,resp);
|
||||
sal_op_release(op);
|
||||
}
|
||||
return;
|
||||
error:
|
||||
resp = belle_sip_response_create_from_request(req, errcode);
|
||||
add_message_accept((belle_sip_message_t*)resp);
|
||||
belle_sip_server_transaction_send_response(server_transaction,resp);
|
||||
sal_op_release(op);
|
||||
}
|
||||
|
||||
static void process_request_event(void *op_base, const belle_sip_request_event_t *event) {
|
||||
|
|
|
|||
|
|
@ -1169,7 +1169,7 @@ static bool_t is_duplicate_msg(LinphoneCore *lc, const char *msg_id){
|
|||
}
|
||||
|
||||
|
||||
static void text_received(SalOp *op, const SalMessage *msg){
|
||||
static void message_received(SalOp *op, const SalMessage *msg){
|
||||
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
|
||||
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
|
||||
LinphoneReason reason = lc->chat_deny_code;
|
||||
|
|
@ -1180,20 +1180,6 @@ static void text_received(SalOp *op, const SalMessage *msg){
|
|||
if (!call) sal_op_release(op);
|
||||
}
|
||||
|
||||
static void is_composing_received(SalOp *op, const SalIsComposing *is_composing) {
|
||||
LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
|
||||
LinphoneReason reason = linphone_core_is_composing_received(lc, op, is_composing);
|
||||
sal_message_reply(op, linphone_reason_to_sal(reason));
|
||||
sal_op_release(op);
|
||||
}
|
||||
|
||||
static void imdn_received(SalOp *op, const SalImdn *imdn) {
|
||||
LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
|
||||
LinphoneReason reason = linphone_core_imdn_received(lc, op, imdn);
|
||||
sal_message_reply(op, linphone_reason_to_sal(reason));
|
||||
sal_op_release(op);
|
||||
}
|
||||
|
||||
static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) {
|
||||
linphone_notify_parse_presence(content_type, content_subtype, body, result);
|
||||
}
|
||||
|
|
@ -1348,19 +1334,19 @@ static void notify_refer(SalOp *op, SalReferStatus status){
|
|||
}
|
||||
}
|
||||
|
||||
static LinphoneChatMessageState chatStatusSal2Linphone(SalTextDeliveryStatus status){
|
||||
static LinphoneChatMessageState chatStatusSal2Linphone(SalMessageDeliveryStatus status){
|
||||
switch(status){
|
||||
case SalTextDeliveryInProgress:
|
||||
case SalMessageDeliveryInProgress:
|
||||
return LinphoneChatMessageStateInProgress;
|
||||
case SalTextDeliveryDone:
|
||||
case SalMessageDeliveryDone:
|
||||
return LinphoneChatMessageStateDelivered;
|
||||
case SalTextDeliveryFailed:
|
||||
case SalMessageDeliveryFailed:
|
||||
return LinphoneChatMessageStateNotDelivered;
|
||||
}
|
||||
return LinphoneChatMessageStateIdle;
|
||||
}
|
||||
|
||||
static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){
|
||||
static void message_delivery_update(SalOp *op, SalMessageDeliveryStatus status){
|
||||
LinphoneChatMessage *chat_msg=(LinphoneChatMessage* )sal_op_get_user_pointer(op);
|
||||
|
||||
if (chat_msg == NULL) {
|
||||
|
|
@ -1371,7 +1357,7 @@ static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){
|
|||
if (chat_msg->chat_room != NULL) {
|
||||
linphone_chat_message_update_state(chat_msg, chatStatusSal2Linphone(status));
|
||||
}
|
||||
if (status != SalTextDeliveryInProgress) { /*only release op if not in progress*/
|
||||
if (status != SalMessageDeliveryInProgress) { /*only release op if not in progress*/
|
||||
linphone_chat_message_destroy(chat_msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -1507,10 +1493,8 @@ SalCallbacks linphone_sal_callbacks={
|
|||
vfu_request,
|
||||
dtmf_received,
|
||||
refer_received,
|
||||
text_received,
|
||||
text_delivery_update,
|
||||
is_composing_received,
|
||||
imdn_received,
|
||||
message_received,
|
||||
message_delivery_update,
|
||||
notify_refer,
|
||||
subscribe_received,
|
||||
incoming_subscribe_closed,
|
||||
|
|
|
|||
253
coreapi/chat.c
253
coreapi/chat.c
|
|
@ -41,6 +41,8 @@ static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr)
|
|||
static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *cr);
|
||||
static void linphone_chat_room_delete_remote_composing_refresh_timer(LinphoneChatRoom *cr);
|
||||
static void _linphone_chat_message_destroy(LinphoneChatMessage *msg);
|
||||
static void linphone_chat_room_notify_is_composing(LinphoneChatRoom *cr, const char *text);
|
||||
static void linphone_chat_room_notify_imdn(LinphoneChatRoom *cr, const char *text);
|
||||
|
||||
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessageCbs);
|
||||
|
||||
|
|
@ -511,6 +513,97 @@ void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc,
|
|||
linphone_chat_message_send_delivery_notification(msg, LinphoneReasonNone);
|
||||
}
|
||||
|
||||
static bool_t is_file_transfer(const char *content_type) {
|
||||
return (strcmp("application/vnd.gsma.rcs-ft-http+xml", content_type) == 0);
|
||||
}
|
||||
|
||||
static bool_t is_im_iscomposing(const char* content_type) {
|
||||
return (strcmp("application/im-iscomposing+xml", content_type) == 0);
|
||||
}
|
||||
|
||||
static bool_t is_imdn(const char *content_type) {
|
||||
return (strcmp("message/imdn+xml", content_type) == 0);
|
||||
}
|
||||
|
||||
static void create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(LinphoneChatMessage *msg) {
|
||||
xmlChar *file_url = NULL;
|
||||
xmlDocPtr xmlMessageBody;
|
||||
xmlNodePtr cur;
|
||||
/* parse the msg body to get all informations from it */
|
||||
xmlMessageBody = xmlParseDoc((const xmlChar *)msg->message);
|
||||
msg->file_transfer_information = linphone_content_new();
|
||||
|
||||
cur = xmlDocGetRootElement(xmlMessageBody);
|
||||
if (cur != NULL) {
|
||||
cur = cur->xmlChildrenNode;
|
||||
while (cur != NULL) {
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) {
|
||||
/* we found a file info node, check if it has a type="file" attribute */
|
||||
xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
|
||||
if (!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for */
|
||||
cur = cur->xmlChildrenNode; /* now loop on the content of the file-info node */
|
||||
while (cur != NULL) {
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-size")) {
|
||||
xmlChar *fileSizeString = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
linphone_content_set_size(msg->file_transfer_information, strtol((const char *)fileSizeString, NULL, 10));
|
||||
xmlFree(fileSizeString);
|
||||
}
|
||||
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-name")) {
|
||||
xmlChar *filename = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
linphone_content_set_name(msg->file_transfer_information, (char *)filename);
|
||||
xmlFree(filename);
|
||||
}
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"content-type")) {
|
||||
xmlChar *contentType = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
int contentTypeIndex = 0;
|
||||
char *type;
|
||||
char *subtype;
|
||||
while (contentType[contentTypeIndex] != '/' && contentType[contentTypeIndex] != '\0') {
|
||||
contentTypeIndex++;
|
||||
}
|
||||
type = ms_strndup((char *)contentType, contentTypeIndex);
|
||||
subtype = ms_strdup(((char *)contentType + contentTypeIndex + 1));
|
||||
linphone_content_set_type(msg->file_transfer_information, type);
|
||||
linphone_content_set_subtype(msg->file_transfer_information, subtype);
|
||||
ms_free(subtype);
|
||||
ms_free(type);
|
||||
xmlFree(contentType);
|
||||
}
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) {
|
||||
file_url = xmlGetProp(cur, (const xmlChar *)"url");
|
||||
}
|
||||
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-key")) {
|
||||
/* there is a key in the msg: file has been encrypted */
|
||||
/* convert the key from base 64 */
|
||||
xmlChar *keyb64 = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
size_t keyLength = b64_decode((char *)keyb64, strlen((char *)keyb64), NULL, 0);
|
||||
uint8_t *keyBuffer = (uint8_t *)malloc(keyLength);
|
||||
/* decode the key into local key buffer */
|
||||
b64_decode((char *)keyb64, strlen((char *)keyb64), keyBuffer, keyLength);
|
||||
linphone_content_set_key(msg->file_transfer_information, (char *)keyBuffer, keyLength);
|
||||
/* duplicate key value into the linphone content private structure */
|
||||
xmlFree(keyb64);
|
||||
free(keyBuffer);
|
||||
}
|
||||
|
||||
cur = cur->next;
|
||||
}
|
||||
xmlFree(typeAttribute);
|
||||
break;
|
||||
}
|
||||
xmlFree(typeAttribute);
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
xmlFreeDoc(xmlMessageBody);
|
||||
|
||||
linphone_chat_message_set_external_body_url(msg, (const char *)file_url);
|
||||
xmlFree(file_url);
|
||||
}
|
||||
|
||||
LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *sal_msg) {
|
||||
LinphoneChatRoom *cr = NULL;
|
||||
LinphoneAddress *addr;
|
||||
|
|
@ -525,18 +618,18 @@ LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const
|
|||
linphone_address_clean(addr);
|
||||
cr = linphone_core_get_chat_room(lc, addr);
|
||||
|
||||
msg = linphone_chat_room_create_message(cr, sal_msg->text); /* create a msg with empty body */
|
||||
msg->content_type = ms_strdup(sal_msg->content_type); /* add the content_type "application/vnd.gsma.rcs-ft-http+xml" */
|
||||
msg = linphone_chat_room_create_message(cr, sal_msg->text);
|
||||
msg->content_type = ms_strdup(sal_msg->content_type);
|
||||
linphone_chat_message_set_from(msg, cr->peer_url);
|
||||
|
||||
to = sal_op_get_to(op) ? linphone_address_new(sal_op_get_to(op)) : linphone_address_new(linphone_core_get_identity(lc));
|
||||
msg->to = to;
|
||||
|
||||
|
||||
msg->time = sal_msg->time;
|
||||
msg->state = LinphoneChatMessageStateDelivered;
|
||||
msg->dir = LinphoneChatMessageIncoming;
|
||||
msg->message_id = ms_strdup(sal_op_get_call_id(op));
|
||||
|
||||
|
||||
ch = sal_op_get_recv_custom_header(op);
|
||||
if (ch) {
|
||||
msg->custom_headers = sal_custom_header_clone(ch);
|
||||
|
|
@ -545,7 +638,7 @@ LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const
|
|||
if (sal_msg->url) {
|
||||
linphone_chat_message_set_external_body_url(msg, sal_msg->url);
|
||||
}
|
||||
|
||||
|
||||
if (imee) {
|
||||
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
|
||||
LinphoneImEncryptionEngineCbsIncomingMessageCb cb_process_incoming_message = linphone_im_encryption_engine_cbs_get_process_incoming_message(imee_cbs);
|
||||
|
|
@ -553,101 +646,26 @@ LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const
|
|||
retval = cb_process_incoming_message(imee, cr, msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (retval <= 0 && strcmp("text/plain", msg->content_type) != 0 && strcmp("message/external-body", msg->content_type) != 0
|
||||
&& strcmp("application/vnd.gsma.rcs-ft-http+xml", msg->content_type) != 0) {
|
||||
|
||||
if ((retval <= 0) && (linphone_core_is_content_type_supported(lc, msg->content_type) == FALSE)) {
|
||||
retval = 415;
|
||||
ms_error("Unsupported MESSAGE (content-type %s not recognized)", msg->content_type);
|
||||
}
|
||||
|
||||
|
||||
if (retval > 0) {
|
||||
reason = linphone_error_code_to_reason(retval);
|
||||
linphone_chat_message_send_delivery_notification(msg, reason);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp("application/vnd.gsma.rcs-ft-http+xml", msg->content_type) == 0) {
|
||||
xmlChar *file_url = NULL;
|
||||
xmlDocPtr xmlMessageBody;
|
||||
xmlNodePtr cur;
|
||||
/* content_type field is, for now, used only for rcs file transfer but we shall strcmp it with "application/vnd.gsma.rcs-ft-http+xml" */
|
||||
/* parse the msg body to get all informations from it */
|
||||
xmlMessageBody = xmlParseDoc((const xmlChar *)msg->message);
|
||||
msg->file_transfer_information = linphone_content_new();
|
||||
|
||||
cur = xmlDocGetRootElement(xmlMessageBody);
|
||||
if (cur != NULL) {
|
||||
cur = cur->xmlChildrenNode;
|
||||
while (cur != NULL) {
|
||||
if (!xmlStrcmp(
|
||||
cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check it has a
|
||||
type="file" attribute */
|
||||
xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
|
||||
if (!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for */
|
||||
cur = cur->xmlChildrenNode; /* now loop on the content of the file-info node */
|
||||
while (cur != NULL) {
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-size")) {
|
||||
xmlChar *fileSizeString = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
linphone_content_set_size(msg->file_transfer_information,
|
||||
strtol((const char *)fileSizeString, NULL, 10));
|
||||
xmlFree(fileSizeString);
|
||||
}
|
||||
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-name")) {
|
||||
xmlChar *filename = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
linphone_content_set_name(
|
||||
msg->file_transfer_information,
|
||||
(char *)filename);
|
||||
xmlFree(filename);
|
||||
}
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"content-type")) {
|
||||
xmlChar *contentType = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
int contentTypeIndex = 0;
|
||||
char *type;
|
||||
char *subtype;
|
||||
while (contentType[contentTypeIndex] != '/' && contentType[contentTypeIndex] != '\0') {
|
||||
contentTypeIndex++;
|
||||
}
|
||||
type = ms_strndup((char *)contentType, contentTypeIndex);
|
||||
subtype = ms_strdup(((char *)contentType + contentTypeIndex + 1));
|
||||
linphone_content_set_type(msg->file_transfer_information, type);
|
||||
linphone_content_set_subtype(msg->file_transfer_information, subtype);
|
||||
ms_free(subtype);
|
||||
ms_free(type);
|
||||
xmlFree(contentType);
|
||||
}
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) {
|
||||
file_url = xmlGetProp(cur, (const xmlChar *)"url");
|
||||
}
|
||||
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-key")) {
|
||||
/* there is a key in the msg: file has been encrypted */
|
||||
/* convert the key from base 64 */
|
||||
xmlChar *keyb64 = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
size_t keyLength = b64_decode((char *)keyb64, strlen((char *)keyb64), NULL, 0);
|
||||
uint8_t *keyBuffer = (uint8_t *)malloc(keyLength);
|
||||
/* decode the key into local key buffer */
|
||||
b64_decode((char *)keyb64, strlen((char *)keyb64), keyBuffer, keyLength);
|
||||
linphone_content_set_key(msg->file_transfer_information, (char *)keyBuffer, keyLength);
|
||||
/* duplicate key value into the linphone content private structure */
|
||||
xmlFree(keyb64);
|
||||
free(keyBuffer);
|
||||
}
|
||||
|
||||
cur = cur->next;
|
||||
}
|
||||
xmlFree(typeAttribute);
|
||||
break;
|
||||
}
|
||||
xmlFree(typeAttribute);
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
xmlFreeDoc(xmlMessageBody);
|
||||
|
||||
linphone_chat_message_set_external_body_url(msg, (const char *)file_url);
|
||||
xmlFree(file_url);
|
||||
if (is_file_transfer(msg->content_type)) {
|
||||
create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(msg);
|
||||
} else if (is_im_iscomposing(msg->content_type)) {
|
||||
linphone_chat_room_notify_is_composing(cr, msg->message);
|
||||
goto end;
|
||||
} else if (is_imdn(msg->content_type)) {
|
||||
linphone_chat_room_notify_imdn(cr, msg->message);
|
||||
goto end;
|
||||
}
|
||||
|
||||
msg->storage_id = linphone_chat_message_store(msg);
|
||||
|
|
@ -744,36 +762,6 @@ static void linphone_chat_room_notify_is_composing(LinphoneChatRoom *cr, const c
|
|||
linphone_xmlparsing_context_destroy(xml_ctx);
|
||||
}
|
||||
|
||||
LinphoneReason linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalIsComposing *is_composing) {
|
||||
LinphoneAddress *addr = linphone_address_new(is_composing->from);
|
||||
LinphoneChatRoom *cr = _linphone_core_get_chat_room(lc, addr);
|
||||
LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(lc);
|
||||
LinphoneReason reason = LinphoneReasonNone;
|
||||
|
||||
if (cr != NULL) {
|
||||
int retval = -1;
|
||||
LinphoneChatMessage *msg = linphone_chat_room_create_message(cr, is_composing->text);
|
||||
linphone_chat_message_set_from_address(msg, addr);
|
||||
msg->content_type = ms_strdup("application/im-iscomposing+xml");
|
||||
if (imee) {
|
||||
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
|
||||
LinphoneImEncryptionEngineCbsIncomingMessageCb cb_process_incoming_message = linphone_im_encryption_engine_cbs_get_process_incoming_message(imee_cbs);
|
||||
if (cb_process_incoming_message) {
|
||||
retval = cb_process_incoming_message(imee, cr, msg);
|
||||
}
|
||||
}
|
||||
if (retval <= 0) {
|
||||
linphone_chat_room_notify_is_composing(cr, msg->message);
|
||||
} else {
|
||||
reason = linphone_error_code_to_reason(retval);
|
||||
}
|
||||
linphone_chat_message_unref(msg);
|
||||
}
|
||||
linphone_address_unref(addr);
|
||||
|
||||
return reason;
|
||||
}
|
||||
|
||||
bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) {
|
||||
return (cr->remote_is_composing == LinphoneIsComposingActive) ? TRUE : FALSE;
|
||||
}
|
||||
|
|
@ -858,16 +846,6 @@ static void linphone_chat_room_notify_imdn(LinphoneChatRoom *cr, const char *tex
|
|||
linphone_xmlparsing_context_destroy(xml_ctx);
|
||||
}
|
||||
|
||||
LinphoneReason linphone_core_imdn_received(LinphoneCore *lc, SalOp *op, const SalImdn *imdn) {
|
||||
LinphoneAddress *addr = linphone_address_new(imdn->from);
|
||||
LinphoneChatRoom *cr = _linphone_core_get_chat_room(lc, addr);
|
||||
if (cr != NULL) {
|
||||
linphone_chat_room_notify_imdn(cr, imdn->content);
|
||||
}
|
||||
linphone_address_unref(addr);
|
||||
return LinphoneReasonNone;
|
||||
}
|
||||
|
||||
LinphoneCore *linphone_chat_room_get_lc(LinphoneChatRoom *cr) {
|
||||
return linphone_chat_room_get_core(cr);
|
||||
}
|
||||
|
|
@ -886,7 +864,7 @@ LinphoneChatMessage *linphone_chat_room_create_message(LinphoneChatRoom *cr, con
|
|||
msg->callbacks = linphone_chat_message_cbs_new();
|
||||
msg->chat_room = (LinphoneChatRoom *)cr;
|
||||
msg->message = message ? ms_strdup(message) : NULL;
|
||||
msg->content_type = NULL; /* this property is used only when transfering file */
|
||||
msg->content_type = ms_strdup("text/plain");
|
||||
msg->file_transfer_information = NULL; /* this property is used only when transfering file */
|
||||
msg->http_request = NULL;
|
||||
msg->time = ms_time(0);
|
||||
|
|
@ -1432,6 +1410,17 @@ void linphone_chat_message_set_external_body_url(LinphoneChatMessage *msg, const
|
|||
msg->external_body_url = url ? ms_strdup(url) : NULL;
|
||||
}
|
||||
|
||||
const char * linphone_chat_message_get_content_type(const LinphoneChatMessage *msg) {
|
||||
return msg->content_type;
|
||||
}
|
||||
|
||||
void linphone_chat_message_set_content_type(LinphoneChatMessage *msg, const char *content_type) {
|
||||
if (msg->content_type) {
|
||||
ms_free(msg->content_type);
|
||||
}
|
||||
msg->content_type = content_type ? ms_strdup(content_type) : NULL;
|
||||
}
|
||||
|
||||
const char *linphone_chat_message_get_appdata(const LinphoneChatMessage *msg) {
|
||||
return msg->appdata;
|
||||
}
|
||||
|
|
|
|||
224
coreapi/lime.c
224
coreapi/lime.c
|
|
@ -89,7 +89,7 @@ uint8_t lime_byteToChar(uint8_t inputByte) {
|
|||
* @param[in] inputString The input string buffer, must be hexadecimal(it is not checked by function, any non hexa char is converted to 0)
|
||||
* @param[in] inputStringLength The lenght in chars of the string buffer, output is half this length
|
||||
*/
|
||||
void lime_strToUint8(uint8_t *outputBytes, uint8_t *inputString, uint16_t inputStringLength) {
|
||||
void lime_strToUint8(uint8_t *outputBytes, const uint8_t *inputString, uint16_t inputStringLength) {
|
||||
int i;
|
||||
for (i=0; i<inputStringLength/2; i++) {
|
||||
outputBytes[i] = (lime_charToByte(inputString[2*i]))<<4 | lime_charToByte(inputString[2*i+1]);
|
||||
|
|
@ -481,7 +481,7 @@ void lime_freeKeys(limeURIKeys_t *associatedKeys) {
|
|||
associatedKeys->peerURI = NULL;
|
||||
}
|
||||
|
||||
int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
|
||||
int lime_encryptMessage(limeKey_t *key, const uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
|
||||
uint8_t authenticatedData[28];
|
||||
/* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
|
||||
memcpy(authenticatedData, selfZID, 12);
|
||||
|
|
@ -573,10 +573,11 @@ int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t mess
|
|||
return retval;
|
||||
}
|
||||
|
||||
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output) {
|
||||
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *contentType, uint8_t *message, uint8_t *peerURI, uint8_t **output) {
|
||||
uint8_t selfZidHex[25];
|
||||
uint8_t selfZid[12]; /* same data but in byte buffer */
|
||||
uint32_t encryptedMessageLength;
|
||||
uint32_t encryptedContentTypeLength;
|
||||
limeURIKeys_t associatedKeys;
|
||||
xmlDocPtr xmlOutputMessage;
|
||||
xmlNodePtr rootNode;
|
||||
|
|
@ -592,6 +593,7 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t
|
|||
|
||||
/* encrypted message length is plaintext + 16 for tag */
|
||||
encryptedMessageLength = (uint32_t)strlen((char *)message) + 16;
|
||||
encryptedContentTypeLength = (uint32_t)strlen((char *)contentType) + 16;
|
||||
|
||||
/* retrieve keys associated to the peer URI */
|
||||
associatedKeys.peerURI = (uint8_t *)malloc(strlen((char *)peerURI)+1);
|
||||
|
|
@ -624,17 +626,21 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t
|
|||
xmlNodePtr msgNode;
|
||||
size_t b64Size = 0;
|
||||
unsigned char *encryptedMessageb64;
|
||||
unsigned char *encryptedContentTypeb64;
|
||||
|
||||
/* encrypt message with current key */
|
||||
limeKey_t *currentKey = associatedKeys.peerKeys[i];
|
||||
/* encrypted message include a 16 bytes tag */
|
||||
uint8_t *encryptedMessage = (uint8_t *)ms_malloc(encryptedMessageLength);
|
||||
uint8_t *encryptedContentType = (uint8_t *)ms_malloc(encryptedContentTypeLength);
|
||||
lime_encryptMessage(currentKey, message, (uint32_t)strlen((char *)message), selfZid, encryptedMessage);
|
||||
lime_encryptMessage(currentKey, (const uint8_t *)contentType, (uint32_t)strlen((char *)contentType), selfZid, encryptedContentType);
|
||||
/* add a "msg" node the the output message, doc node is :
|
||||
* <msg>
|
||||
* <pzid>peerZID</pzid>
|
||||
* <index>session index</index>
|
||||
* <text>ciphertext</text>
|
||||
* <content-type>ciphertext</content-type>
|
||||
* </msg> */
|
||||
msgNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"msg", NULL);
|
||||
lime_int8ToStr(peerZidHex, currentKey->peerZID, 12);
|
||||
|
|
@ -661,6 +667,16 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t
|
|||
ms_free(encryptedMessage);
|
||||
ms_free(encryptedMessageb64);
|
||||
|
||||
/* convert the encrypted content-type to base 64 */
|
||||
b64Size = 0;
|
||||
bctbx_base64_encode(NULL, &b64Size, encryptedContentType, encryptedContentTypeLength); /* b64Size is 0, so it is set to the requested output buffer size */
|
||||
encryptedContentTypeb64 = ms_malloc(b64Size+1); /* allocate a buffer of requested size +1 for NULL termination */
|
||||
bctbx_base64_encode(encryptedContentTypeb64, &b64Size, encryptedContentType, encryptedContentTypeLength); /* b64Size is 0, so it is set to the requested output buffer size */
|
||||
encryptedContentTypeb64[b64Size] = '\0'; /* libxml need a null terminated string */
|
||||
xmlNewTextChild(msgNode, NULL, (const xmlChar *)"content-type", (const xmlChar *)encryptedContentTypeb64);
|
||||
ms_free(encryptedContentType);
|
||||
ms_free(encryptedContentTypeb64);
|
||||
|
||||
/* add the message Node into the doc */
|
||||
xmlAddChild(rootNode, msgNode);
|
||||
|
||||
|
|
@ -683,17 +699,21 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t
|
|||
return 0;
|
||||
}
|
||||
|
||||
int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output) {
|
||||
int retval;
|
||||
int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output, char **content_type) {
|
||||
int retval = 0;
|
||||
uint8_t selfZidHex[25];
|
||||
uint8_t selfZid[12]; /* same data but in byte buffer */
|
||||
char xpath_str[MAX_XPATH_LENGTH];
|
||||
limeKey_t associatedKey;
|
||||
xmlChar *peerZidHex = NULL;
|
||||
xmlNodePtr cur;
|
||||
const char *peerZidHex = NULL;
|
||||
xmlparsing_context_t *xml_ctx;
|
||||
xmlXPathObjectPtr msg_object;
|
||||
uint8_t *encryptedMessage = NULL;
|
||||
size_t encryptedMessageLength = 0;
|
||||
uint8_t *encryptedContentType = NULL;
|
||||
size_t encryptedContentTypeLength = 0;
|
||||
uint32_t usedSessionIndex = 0;
|
||||
xmlDocPtr xmlEncryptedMessage;
|
||||
int i;
|
||||
|
||||
if (cacheBuffer == NULL) {
|
||||
return LIME_INVALID_CACHE;
|
||||
|
|
@ -704,81 +724,80 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_
|
|||
}
|
||||
lime_strToUint8(selfZid, selfZidHex, 24);
|
||||
|
||||
/* parse the message into an xml doc */
|
||||
/* make sure we have a valid xml message before trying to parse it */
|
||||
if (memcmp(message, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", 38) != 0) {
|
||||
return LIME_INVALID_ENCRYPTED_MESSAGE;
|
||||
}
|
||||
xmlEncryptedMessage = xmlParseDoc((const xmlChar *)message);
|
||||
if (xmlEncryptedMessage == NULL) {
|
||||
return LIME_INVALID_ENCRYPTED_MESSAGE;
|
||||
xml_ctx = linphone_xmlparsing_context_new();
|
||||
xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
|
||||
xml_ctx->doc = xmlReadDoc((const unsigned char*)message, 0, NULL, 0);
|
||||
if (xml_ctx->doc == NULL) {
|
||||
retval = LIME_INVALID_ENCRYPTED_MESSAGE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* retrieve the sender ZID which is the first child of root */
|
||||
cur = xmlDocGetRootElement(xmlEncryptedMessage);
|
||||
if (cur != NULL) {
|
||||
cur = cur->xmlChildrenNode;
|
||||
if ((!xmlStrcmp(cur->name, (const xmlChar *)"ZID"))){ /* sender ZID found, extract it */
|
||||
peerZidHex = xmlNodeListGetString(xmlEncryptedMessage, cur->xmlChildrenNode, 1);
|
||||
/* convert it from hexa string to bytes string and set the result in the associatedKey structure */
|
||||
lime_strToUint8(associatedKey.peerZID, peerZidHex, (uint16_t)strlen((char *)peerZidHex));
|
||||
cur = cur->next;
|
||||
}
|
||||
if (linphone_create_xml_xpath_context(xml_ctx) < 0) {
|
||||
retval = LIME_INVALID_ENCRYPTED_MESSAGE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Retrieve the sender ZID */
|
||||
peerZidHex = linphone_get_xml_text_content(xml_ctx, "/doc/ZID");
|
||||
if (peerZidHex != NULL) {
|
||||
/* get from cache the matching key */
|
||||
retval = lime_getCachedRcvKeyByZid(cacheBuffer, &associatedKey);
|
||||
/* Convert it from hexa string to bytes string and set the result in the associatedKey structure */
|
||||
lime_strToUint8(associatedKey.peerZID, (const uint8_t *)peerZidHex, (uint16_t)strlen(peerZidHex));
|
||||
linphone_free_xml_text_content(peerZidHex);
|
||||
|
||||
/* Get the matching key from cache */
|
||||
retval = lime_getCachedRcvKeyByZid(cacheBuffer, &associatedKey);
|
||||
if (retval != 0) {
|
||||
xmlFree(peerZidHex);
|
||||
xmlFreeDoc(xmlEncryptedMessage);
|
||||
return retval;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* retrieve the portion of message which is encrypted with our key */
|
||||
while (cur != NULL) { /* loop on all "msg" node in the message */
|
||||
xmlNodePtr msgChildrenNode = cur->xmlChildrenNode;
|
||||
xmlChar *currentZidHex = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1); /* pZID is the first element of msg */
|
||||
if (!xmlStrcmp(currentZidHex, (const xmlChar *)selfZidHex)) { /* we found the msg node we are looking for */
|
||||
/* get the index (second node in the msg one) */
|
||||
xmlChar *sessionIndexHex;
|
||||
xmlChar *encryptedMessageb64;
|
||||
|
||||
msgChildrenNode = msgChildrenNode->next;
|
||||
sessionIndexHex = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
|
||||
usedSessionIndex = (((uint32_t)lime_charToByte(sessionIndexHex[0]))<<28)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[1]))<<24)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[2]))<<20)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[3]))<<16)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[4]))<<12)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[5]))<<8)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[6]))<<4)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[7])));
|
||||
xmlFree(sessionIndexHex);
|
||||
|
||||
/* get the encrypted message */
|
||||
msgChildrenNode = msgChildrenNode->next;
|
||||
|
||||
/* convert the cipherText from base 64 */
|
||||
encryptedMessageb64 = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
|
||||
bctbx_base64_decode(NULL, &encryptedMessageLength, encryptedMessageb64, strlen((char *)encryptedMessageb64)); /* encryptedMessageLength is 0, so it will be set to the requested buffer length */
|
||||
encryptedMessage = (uint8_t *)ms_malloc(encryptedMessageLength);
|
||||
bctbx_base64_decode(encryptedMessage, &encryptedMessageLength, encryptedMessageb64, strlen((char *)encryptedMessageb64));
|
||||
|
||||
xmlFree(encryptedMessageb64);
|
||||
xmlFree(currentZidHex);
|
||||
break;
|
||||
}
|
||||
|
||||
cur = cur->next;
|
||||
xmlFree(currentZidHex);
|
||||
/* Retrieve the portion of message which is encrypted with our key */
|
||||
msg_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/doc/msg");
|
||||
if ((msg_object != NULL) && (msg_object->nodesetval != NULL)) {
|
||||
for (i = 1; i <= msg_object->nodesetval->nodeNr; i++) {
|
||||
const char *currentZidHex;
|
||||
const char *sessionIndexHex;
|
||||
const char *encryptedMessageb64;
|
||||
const char *encryptedContentTypeb64;
|
||||
snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/pzid", i);
|
||||
currentZidHex = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
||||
if ((currentZidHex != NULL) && (strcmp(currentZidHex, (char *)selfZidHex) == 0)) {
|
||||
/* We found the msg node we are looking for */
|
||||
snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/index", i);
|
||||
sessionIndexHex = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
||||
if (sessionIndexHex != NULL) {
|
||||
usedSessionIndex = (((uint32_t)lime_charToByte(sessionIndexHex[0]))<<28)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[1]))<<24)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[2]))<<20)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[3]))<<16)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[4]))<<12)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[5]))<<8)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[6]))<<4)
|
||||
| (((uint32_t)lime_charToByte(sessionIndexHex[7])));
|
||||
linphone_free_xml_text_content(sessionIndexHex);
|
||||
}
|
||||
snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/text", i);
|
||||
encryptedMessageb64 = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
||||
if (encryptedMessageb64 != NULL) {
|
||||
bctbx_base64_decode(NULL, &encryptedMessageLength, (const unsigned char *)encryptedMessageb64, strlen(encryptedMessageb64)); /* encryptedMessageLength is 0, so it will be set to the requested buffer length */
|
||||
encryptedMessage = (uint8_t *)ms_malloc(encryptedMessageLength);
|
||||
bctbx_base64_decode(encryptedMessage, &encryptedMessageLength, (const unsigned char *)encryptedMessageb64, strlen(encryptedMessageb64));
|
||||
linphone_free_xml_text_content(encryptedMessageb64);
|
||||
}
|
||||
snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/content-type", i);
|
||||
encryptedContentTypeb64 = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
||||
if (encryptedContentTypeb64 != NULL) {
|
||||
bctbx_base64_decode(NULL, &encryptedContentTypeLength, (const unsigned char *)encryptedContentTypeb64, strlen(encryptedContentTypeb64)); /* encryptedContentTypeLength is 0, so it will be set to the requested buffer length */
|
||||
encryptedContentType = (uint8_t *)ms_malloc(encryptedContentTypeLength);
|
||||
bctbx_base64_decode(encryptedContentType, &encryptedContentTypeLength, (const unsigned char *)encryptedContentTypeb64, strlen(encryptedContentTypeb64));
|
||||
linphone_free_xml_text_content(encryptedContentTypeb64);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (currentZidHex != NULL) linphone_free_xml_text_content(currentZidHex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xmlFree(peerZidHex);
|
||||
xmlFreeDoc(xmlEncryptedMessage);
|
||||
|
||||
/* do we have retrieved correctly all the needed data */
|
||||
if (encryptedMessage == NULL) {
|
||||
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
|
||||
|
|
@ -801,23 +820,35 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_
|
|||
lime_deriveKey(&associatedKey);
|
||||
}
|
||||
|
||||
/* decrypt the message */
|
||||
*output = (uint8_t *)ms_malloc(encryptedMessageLength - 16 +1); /* plain message is same length than encrypted one with 16 bytes less for the tag + 1 to add the null termination char */
|
||||
/* Decrypt the message */
|
||||
*output = (uint8_t *)ms_malloc(encryptedMessageLength - 16 + 1); /* plain message is same length than encrypted one with 16 bytes less for the tag + 1 to add the null termination char */
|
||||
retval = lime_decryptMessage(&associatedKey, encryptedMessage, (uint32_t)encryptedMessageLength, selfZid, *output);
|
||||
|
||||
ms_free(encryptedMessage);
|
||||
|
||||
if (retval != 0) {
|
||||
ms_free(*output);
|
||||
*output = NULL;
|
||||
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
|
||||
}
|
||||
|
||||
/* Decrypt the content-type */
|
||||
if (encryptedContentType != NULL) {
|
||||
*content_type = (char *)ms_malloc(encryptedContentTypeLength - 16 + 1); /* content-type is same length than encrypted one with 16 bytes less for the tag + 1 to add the null termination char */
|
||||
retval = lime_decryptMessage(&associatedKey, encryptedContentType, (uint32_t)encryptedContentTypeLength, selfZid, *((uint8_t **)content_type));
|
||||
ms_free(encryptedContentType);
|
||||
if (retval != 0) {
|
||||
ms_free(*content_type);
|
||||
*content_type = NULL;
|
||||
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/* update used key */
|
||||
lime_deriveKey(&associatedKey);
|
||||
lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
linphone_xmlparsing_context_destroy(xml_ctx);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr) {
|
||||
|
|
@ -887,6 +918,7 @@ int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEn
|
|||
int retval;
|
||||
xmlDocPtr cacheXml;
|
||||
uint8_t *decrypted_body = NULL;
|
||||
char *decrypted_content_type = NULL;
|
||||
|
||||
cacheString=ms_load_file_content(CACHEFD, &cacheSize);
|
||||
if (!cacheString){
|
||||
|
|
@ -899,7 +931,7 @@ int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEn
|
|||
fclose(CACHEFD);
|
||||
cacheXml = xmlParseDoc((xmlChar*)cacheString);
|
||||
ms_free(cacheString);
|
||||
retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)msg->message, &decrypted_body);
|
||||
retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)msg->message, &decrypted_body, &decrypted_content_type);
|
||||
if (retval != 0) {
|
||||
ms_warning("Unable to decrypt message, reason : %s", lime_error_code_to_string(retval));
|
||||
if (decrypted_body) ms_free(decrypted_body);
|
||||
|
|
@ -922,13 +954,15 @@ int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEn
|
|||
ms_free(msg->message);
|
||||
}
|
||||
msg->message = (char *)decrypted_body;
|
||||
|
||||
if (strcmp("application/cipher.vnd.gsma.rcs-ft-http+xml", msg->content_type) == 0) {
|
||||
ms_free(msg->content_type);
|
||||
msg->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml");
|
||||
|
||||
if (decrypted_content_type != NULL) {
|
||||
linphone_chat_message_set_content_type(msg, decrypted_content_type);
|
||||
} else {
|
||||
ms_free(msg->content_type);
|
||||
msg->content_type = ms_strdup("text/plain");
|
||||
if (strcmp("application/cipher.vnd.gsma.rcs-ft-http+xml", msg->content_type) == 0) {
|
||||
linphone_chat_message_set_content_type(msg, "application/vnd.gsma.rcs-ft-http+xml");
|
||||
} else {
|
||||
linphone_chat_message_set_content_type(msg, "text/plain");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -941,25 +975,22 @@ int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEn
|
|||
int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
|
||||
LinphoneCore *lc = linphone_im_encryption_engine_get_core(engine);
|
||||
int errcode = -1;
|
||||
char *content_type = "xml/cipher";
|
||||
char *new_content_type = "xml/cipher";
|
||||
if(linphone_core_lime_enabled(room->lc)) {
|
||||
if (linphone_chat_room_lime_available(room)) {
|
||||
if (msg->content_type) {
|
||||
if (strcmp(msg->content_type, "application/vnd.gsma.rcs-ft-http+xml") == 0) {
|
||||
/* it's a file transfer, content type shall be set to
|
||||
application/cipher.vnd.gsma.rcs-ft-http+xml*/
|
||||
content_type = "application/cipher.vnd.gsma.rcs-ft-http+xml";
|
||||
/* It's a file transfer, content type shall be set to application/cipher.vnd.gsma.rcs-ft-http+xml
|
||||
TODO: As of january 2017, the content type is now included in the encrypted body, this
|
||||
application/cipher.vnd.gsma.rcs-ft-http+xml is kept for compatibility with previous versions,
|
||||
but may be dropped in the future to use xml/cipher instead. */
|
||||
new_content_type = "application/cipher.vnd.gsma.rcs-ft-http+xml";
|
||||
} else if (strcmp(msg->content_type, "application/im-iscomposing+xml") == 0) {
|
||||
/* We don't encrypt composing messages */
|
||||
return errcode;
|
||||
} else if (strcmp(msg->content_type, "message/imdn+xml") == 0) {
|
||||
/* We don't encrypt imdn messages */
|
||||
return errcode;
|
||||
}
|
||||
}
|
||||
msg->content_type = ms_strdup(content_type);
|
||||
|
||||
|
||||
|
||||
/* access the zrtp cache to get keys needed to cipher the message */
|
||||
const char *zrtp_secrets_cache = linphone_core_get_zrtp_secrets_file(lc);
|
||||
FILE *CACHEFD = fopen(zrtp_secrets_cache, "rb+");
|
||||
|
|
@ -986,7 +1017,7 @@ int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneImEncryptionEn
|
|||
fclose(CACHEFD);
|
||||
cacheXml = xmlParseDoc((xmlChar*)cacheString);
|
||||
ms_free(cacheString);
|
||||
retval = lime_createMultipartMessage(cacheXml, (uint8_t *)msg->message, (uint8_t *)peer, &crypted_body);
|
||||
retval = lime_createMultipartMessage(cacheXml, msg->content_type, (uint8_t *)msg->message, (uint8_t *)peer, &crypted_body);
|
||||
if (retval != 0) {
|
||||
ms_warning("Unable to encrypt message for %s : %s", peer, lime_error_code_to_string(retval));
|
||||
if (crypted_body) ms_free(crypted_body);
|
||||
|
|
@ -1007,6 +1038,7 @@ int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneImEncryptionEn
|
|||
ms_free(msg->message);
|
||||
}
|
||||
msg->message = (char *)crypted_body;
|
||||
msg->content_type = ms_strdup(new_content_type);
|
||||
}
|
||||
ms_free(peer);
|
||||
xmlFreeDoc(cacheXml);
|
||||
|
|
@ -1070,14 +1102,14 @@ void lime_im_encryption_engine_generate_file_transfer_key_cb(LinphoneImEncryptio
|
|||
bool_t lime_is_available() { return FALSE; }
|
||||
int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) { return LIME_NOT_ENABLED;}
|
||||
int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output) { return LIME_NOT_ENABLED;}
|
||||
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output) { return LIME_NOT_ENABLED;}
|
||||
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *content_type, uint8_t *message, uint8_t *peerURI, uint8_t **output) { return LIME_NOT_ENABLED;}
|
||||
int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {return LIME_NOT_ENABLED;}
|
||||
void lime_freeKeys(limeURIKeys_t *associatedKeys){
|
||||
}
|
||||
int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedKeys){
|
||||
return LIME_NOT_ENABLED;
|
||||
}
|
||||
int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
|
||||
int lime_encryptMessage(limeKey_t *key, const uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
|
||||
return LIME_NOT_ENABLED;
|
||||
}
|
||||
int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role) {
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ LINPHONE_PUBLIC void lime_freeKeys(limeURIKeys_t *associatedKeys);
|
|||
* @return 0 on success, error code otherwise
|
||||
*
|
||||
*/
|
||||
LINPHONE_PUBLIC int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage);
|
||||
LINPHONE_PUBLIC int lime_encryptMessage(limeKey_t *key, const uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage);
|
||||
|
||||
/**
|
||||
* @brief Encrypt a file before transfering it to the server, encryption is done in several call, first one will be done with cryptoContext null, last one with length = 0
|
||||
|
|
@ -170,13 +170,14 @@ LINPHONE_PUBLIC int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessag
|
|||
* Retrieve in cache the needed keys which are then updated. Output buffer is allocated and must be freed by caller
|
||||
*
|
||||
* @param[in,out] cacheBuffer The xmlDoc containing current cache, get the keys and selfZID from it, updated by this function with derivated keys
|
||||
* @param[in] message The plain text message to be encrypted
|
||||
* @param[in] content_type The content type of the message to encrypt
|
||||
* @param[in] message The message content to be encrypted
|
||||
* @param[in] peerURI The destination URI, associated keys will be found in cache
|
||||
* @param[out] output The output buffer, allocated and set with the encrypted message xml body(null terminated string). Must be freed by caller
|
||||
*
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
LINPHONE_PUBLIC int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output);
|
||||
LINPHONE_PUBLIC int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *content_type, uint8_t *message, uint8_t *peerURI, uint8_t **output);
|
||||
|
||||
/**
|
||||
* @brief decrypt a multipart xml message
|
||||
|
|
@ -185,11 +186,11 @@ LINPHONE_PUBLIC int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *
|
|||
* @param[in,out] cacheBuffer The xmlDoc containing current cache, get the key and selfZID from it, updated by this function with derivated keys
|
||||
* @param[in] message The multipart message, contain one or several part identified by destination ZID, one shall match the self ZID retrieved from cache
|
||||
* @param[out] output The output buffer, allocated and set with the decrypted message(null terminated string). Must be freed by caller
|
||||
*
|
||||
* @param[out] content_type The content type of the decrypted message
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
|
||||
LINPHONE_PUBLIC int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output);
|
||||
LINPHONE_PUBLIC int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output, char **content_type);
|
||||
|
||||
/**
|
||||
* @brief given a readable version of error code generated by Lime functions
|
||||
|
|
|
|||
|
|
@ -1997,9 +1997,10 @@ static void linphone_core_init(LinphoneCore * lc, LinphoneCoreCbs *cbs, LpConfig
|
|||
#ifdef SQLITE_STORAGE_ENABLED
|
||||
sqlite3_bctbx_vfs_register(0);
|
||||
#endif
|
||||
|
||||
|
||||
lc->vcard_context = linphone_vcard_context_new();
|
||||
|
||||
linphone_core_initialize_supported_content_types(lc);
|
||||
|
||||
remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
|
||||
if (remote_provisioning_uri == NULL) {
|
||||
linphone_configuring_terminated(lc, LinphoneConfiguringSkipped, NULL);
|
||||
|
|
@ -7199,3 +7200,19 @@ void linphone_core_set_im_encryption_engine(LinphoneCore *lc, LinphoneImEncrypti
|
|||
LinphoneImEncryptionEngine *linphone_core_get_im_encryption_engine(const LinphoneCore *lc) {
|
||||
return lc->im_encryption_engine;
|
||||
}
|
||||
|
||||
void linphone_core_initialize_supported_content_types(LinphoneCore *lc) {
|
||||
sal_add_content_type_support(lc->sal, "text/plain");
|
||||
sal_add_content_type_support(lc->sal, "message/external-body");
|
||||
sal_add_content_type_support(lc->sal, "application/vnd.gsma.rcs-ft-http+xml");
|
||||
sal_add_content_type_support(lc->sal, "application/im-iscomposing+xml");
|
||||
sal_add_content_type_support(lc->sal, "message/imdn+xml");
|
||||
}
|
||||
|
||||
bool_t linphone_core_is_content_type_supported(const LinphoneCore *lc, const char *content_type) {
|
||||
return sal_is_content_type_supported(lc->sal, content_type);
|
||||
}
|
||||
|
||||
void linphone_core_add_content_type_support(LinphoneCore *lc, const char *content_type) {
|
||||
sal_add_content_type_support(lc->sal, content_type);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,16 +203,17 @@ static int callback_all(void *data, int argc, char **argv, char **colName){
|
|||
* | 0 | storage_id
|
||||
* | 1 | localContact
|
||||
* | 2 | remoteContact
|
||||
* | 3 | direction flag
|
||||
* | 4 | message
|
||||
* | 5 | time (unused now, used to be string-based timestamp)
|
||||
* | 6 | read flag
|
||||
* | 7 | status
|
||||
* | 8 | external body url
|
||||
* | 3 | direction flag (LinphoneChatMessageDir)
|
||||
* | 4 | message (text content of the message)
|
||||
* | 5 | time (unused now, used to be string-based timestamp, replaced by the utc timestamp)
|
||||
* | 6 | read flag (no longer used, replaced by the LinphoneChatMessageStateDisplayed state)
|
||||
* | 7 | status (LinphoneChatMessageState)
|
||||
* | 8 | external body url (deprecated file transfer system)
|
||||
* | 9 | utc timestamp
|
||||
* | 10 | app data text
|
||||
* | 11 | linphone content id
|
||||
* | 12 | message id
|
||||
* | 11 | linphone content id (LinphoneContent describing a file transfer)
|
||||
* | 12 | message id (used for IMDN)
|
||||
* | 13 | content type (of the message field [must be text representable])
|
||||
*/
|
||||
static int create_chat_message(void *data, int argc, char **argv, char **colName){
|
||||
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
|
||||
|
|
@ -245,6 +246,7 @@ static int create_chat_message(void *data, int argc, char **argv, char **colName
|
|||
new_message->external_body_url= ms_strdup(argv[8]);
|
||||
new_message->appdata = ms_strdup(argv[10]);
|
||||
new_message->message_id = ms_strdup(argv[12]);
|
||||
new_message->content_type = ms_strdup(argv[13]);
|
||||
|
||||
if (argv[11] != NULL) {
|
||||
int id = atoi(argv[11]);
|
||||
|
|
@ -253,6 +255,17 @@ static int create_chat_message(void *data, int argc, char **argv, char **colName
|
|||
}
|
||||
}
|
||||
|
||||
/* Fix content type for old messages that were stored without it */
|
||||
if (new_message->content_type == NULL) {
|
||||
if (new_message->file_transfer_information != NULL) {
|
||||
new_message->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml");
|
||||
} else if (new_message->external_body_url != NULL) {
|
||||
new_message->content_type = ms_strdup("message/external-body");
|
||||
} else {
|
||||
new_message->content_type = ms_strdup("text/plain");
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the new message to the weak messages list. */
|
||||
linphone_chat_room_add_weak_message(cr, new_message);
|
||||
}
|
||||
|
|
@ -330,7 +343,7 @@ unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){
|
|||
|
||||
peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(msg->chat_room));
|
||||
local_contact=linphone_address_as_string_uri_only(linphone_chat_message_get_local_address(msg));
|
||||
buf = sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%lld,%Q,%i,%Q);",
|
||||
buf = sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%lld,%Q,%i,%Q,%Q);",
|
||||
local_contact,
|
||||
peer,
|
||||
msg->dir,
|
||||
|
|
@ -342,7 +355,8 @@ unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){
|
|||
(int64_t)msg->time,
|
||||
msg->appdata,
|
||||
content_id,
|
||||
msg->message_id
|
||||
msg->message_id,
|
||||
msg->content_type
|
||||
);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
|
|
@ -748,13 +762,21 @@ void linphone_update_table(sqlite3* db) {
|
|||
if (ret != SQLITE_OK) {
|
||||
ms_message("Table already up to date: %s", errmsg);
|
||||
} else {
|
||||
ms_message("Table history updated successfully for message_id data.");
|
||||
ms_message("Table history updated successfully for messageId data.");
|
||||
}
|
||||
|
||||
// Convert is_read to LinphoneChatMessageStateDisplayed
|
||||
buf = sqlite3_mprintf("UPDATE history SET status=%i WHERE read=1 AND direction=%i;", LinphoneChatMessageStateDisplayed, LinphoneChatMessageIncoming);
|
||||
linphone_sql_request(db, buf);
|
||||
sqlite3_free(buf);
|
||||
|
||||
/* New field for content type */
|
||||
ret = sqlite3_exec(db, "ALTER TABLE history ADD COLUMN content_type TEXT;", NULL, NULL, &errmsg);
|
||||
if (ret != SQLITE_OK) {
|
||||
ms_message("Table already up to date: %s", errmsg);
|
||||
} else {
|
||||
ms_message("Table history updated successfully for content_type data.");
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
|
||||
|
|
|
|||
|
|
@ -554,8 +554,6 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LinphoneCore *lc
|
|||
void linphone_proxy_config_write_to_config_file(struct _LpConfig* config,LinphoneProxyConfig *obj, int index);
|
||||
|
||||
LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *msg);
|
||||
LinphoneReason linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalIsComposing *is_composing);
|
||||
LinphoneReason linphone_core_imdn_received(LinphoneCore *lc, SalOp *op, const SalImdn *imdn);
|
||||
void linphone_core_real_time_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, uint32_t character, LinphoneCall *call);
|
||||
|
||||
void linphone_call_init_stats(LinphoneCallStats *stats, int type);
|
||||
|
|
@ -1093,9 +1091,7 @@ struct _LinphoneCore
|
|||
char *tls_cert;
|
||||
char *tls_key;
|
||||
|
||||
/*default resource list server*/
|
||||
LinphoneAddress *default_rls_addr;
|
||||
|
||||
LinphoneAddress *default_rls_addr; /*default resource list server*/
|
||||
LinphoneImEncryptionEngine *im_encryption_engine;
|
||||
};
|
||||
|
||||
|
|
@ -1148,6 +1144,8 @@ bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, cons
|
|||
#define linphone_core_ready(lc) ((lc)->state==LinphoneGlobalOn || (lc)->state==LinphoneGlobalShutdown)
|
||||
void _linphone_core_configure_resolver(void);
|
||||
|
||||
void linphone_core_initialize_supported_content_types(LinphoneCore *lc);
|
||||
|
||||
struct _EcCalibrator{
|
||||
MSFactory *factory;
|
||||
ms_thread_t thread;
|
||||
|
|
|
|||
|
|
@ -310,11 +310,13 @@ LINPHONE_PUBLIC bool_t linphone_chat_room_is_remote_composing(const LinphoneChat
|
|||
* @return the number of unread messages.
|
||||
*/
|
||||
LINPHONE_PUBLIC int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr);
|
||||
|
||||
/**
|
||||
* Returns back pointer to #LinphoneCore object.
|
||||
* @deprecated use linphone_chat_room_get_core()
|
||||
**/
|
||||
LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr);
|
||||
|
||||
/**
|
||||
* Returns back pointer to #LinphoneCore object.
|
||||
**/
|
||||
|
|
@ -349,68 +351,83 @@ LINPHONE_PUBLIC unsigned int linphone_chat_message_store(LinphoneChatMessage *ms
|
|||
* Returns a #LinphoneChatMessageState as a string.
|
||||
*/
|
||||
LINPHONE_PUBLIC const char* linphone_chat_message_state_to_string(const LinphoneChatMessageState state);
|
||||
|
||||
/**
|
||||
* Get the state of the message
|
||||
*@param message #LinphoneChatMessage obj
|
||||
*@return #LinphoneChatMessageState
|
||||
*/
|
||||
LINPHONE_PUBLIC LinphoneChatMessageState linphone_chat_message_get_state(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Duplicate a LinphoneChatMessage
|
||||
**/
|
||||
LINPHONE_PUBLIC LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Acquire a reference to the chat message.
|
||||
* @param msg the chat message
|
||||
* @return the same chat message
|
||||
**/
|
||||
LINPHONE_PUBLIC LinphoneChatMessage * linphone_chat_message_ref(LinphoneChatMessage *msg);
|
||||
|
||||
/**
|
||||
* Release reference to the chat message.
|
||||
* @param msg the chat message.
|
||||
**/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_unref(LinphoneChatMessage *msg);
|
||||
|
||||
/**
|
||||
* Destroys a LinphoneChatMessage.
|
||||
**/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_destroy(LinphoneChatMessage* msg);
|
||||
|
||||
/** @deprecated Use linphone_chat_message_set_from_address() instead. */
|
||||
#define linphone_chat_message_set_from(msg, addr) linphone_chat_message_set_from_address(msg, addr)
|
||||
|
||||
/**
|
||||
* Set origin of the message
|
||||
* @param[in] message #LinphoneChatMessage obj
|
||||
* @param[in] from #LinphoneAddress origin of this message (copied)
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_set_from_address(LinphoneChatMessage* message, const LinphoneAddress* from);
|
||||
|
||||
/** @deprecated Use linphone_chat_message_get_from_address() instead. */
|
||||
#define linphone_chat_message_get_from(msg) linphone_chat_message_get_from_address(msg)
|
||||
|
||||
/**
|
||||
* Get origin of the message
|
||||
* @param[in] message #LinphoneChatMessage obj
|
||||
* @return #LinphoneAddress
|
||||
*/
|
||||
LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_message_get_from_address(const LinphoneChatMessage* message);
|
||||
|
||||
#define linphone_chat_message_set_to(msg, addr) linphone_chat_message_set_to_address(msg, addr)
|
||||
|
||||
/**
|
||||
* Set destination of the message
|
||||
* @param[in] message #LinphoneChatMessage obj
|
||||
* @param[in] addr #LinphoneAddress destination of this message (copied)
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_set_to_address(LinphoneChatMessage* message, const LinphoneAddress* addr);
|
||||
|
||||
/** @deprecated Use linphone_chat_message_get_to_address() instead. */
|
||||
#define linphone_chat_message_get_to(msg) linphone_chat_message_get_to_address(msg)
|
||||
|
||||
/**
|
||||
* Get destination of the message
|
||||
* @param[in] message #LinphoneChatMessage obj
|
||||
* @return #LinphoneAddress
|
||||
*/
|
||||
LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_message_get_to_address(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Linphone message can carry external body as defined by rfc2017
|
||||
* @param message #LinphoneChatMessage
|
||||
* @return external body url or NULL if not present.
|
||||
*/
|
||||
LINPHONE_PUBLIC const char* linphone_chat_message_get_external_body_url(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Linphone message can carry external body as defined by rfc2017
|
||||
*
|
||||
|
|
@ -418,6 +435,7 @@ LINPHONE_PUBLIC const char* linphone_chat_message_get_external_body_url(const Li
|
|||
* @param url ex: access-type=URL; URL="http://www.foo.com/file"
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message,const char* url);
|
||||
|
||||
/**
|
||||
* Get the file_transfer_information (used by call backs to recover informations during a rcs file transfer)
|
||||
*
|
||||
|
|
@ -425,6 +443,22 @@ LINPHONE_PUBLIC void linphone_chat_message_set_external_body_url(LinphoneChatMes
|
|||
* @return a pointer to the LinphoneContent structure or NULL if not present.
|
||||
*/
|
||||
LINPHONE_PUBLIC const LinphoneContent* linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Get the content type of a chat message.
|
||||
* @param[in] message LinphoneChatMessage object
|
||||
* @return The content type of the chat message
|
||||
*/
|
||||
LINPHONE_PUBLIC const char * linphone_chat_message_get_content_type(const LinphoneChatMessage *message);
|
||||
|
||||
/**
|
||||
* Set the content type of a chat message.
|
||||
* This content type must match a content that is text representable, such as text/plain, text/html or image/svg+xml.
|
||||
* @param[in] message LinphoneChatMessage object
|
||||
* @param[in] content_type The new content type of the chat message
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_set_content_type(LinphoneChatMessage *message, const char *content_type);
|
||||
|
||||
/**
|
||||
* Start the download of the file from remote server
|
||||
*
|
||||
|
|
@ -434,16 +468,19 @@ LINPHONE_PUBLIC const LinphoneContent* linphone_chat_message_get_file_transfer_i
|
|||
* @deprecated Use linphone_chat_message_download_file() instead.
|
||||
*/
|
||||
LINPHONE_PUBLIC LINPHONE_DEPRECATED void linphone_chat_message_start_file_download(LinphoneChatMessage* message, LinphoneChatMessageStateChangedCb status_cb, void* ud);
|
||||
|
||||
/**
|
||||
* Start the download of the file referenced in a LinphoneChatMessage from remote server.
|
||||
* @param[in] message LinphoneChatMessage object.
|
||||
*/
|
||||
LINPHONE_PUBLIC int linphone_chat_message_download_file(LinphoneChatMessage *message);
|
||||
|
||||
/**
|
||||
* Cancel an ongoing file transfer attached to this message.(upload or download)
|
||||
* @param msg #LinphoneChatMessage
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_cancel_file_transfer(LinphoneChatMessage* msg);
|
||||
|
||||
/**
|
||||
* Linphone message has an app-specific field that can store a text. The application might want
|
||||
* to use it for keeping data over restarts, like thumbnail image path.
|
||||
|
|
@ -451,6 +488,7 @@ LINPHONE_PUBLIC void linphone_chat_message_cancel_file_transfer(LinphoneChatMess
|
|||
* @return the application-specific data or NULL if none has been stored.
|
||||
*/
|
||||
LINPHONE_PUBLIC const char* linphone_chat_message_get_appdata(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Linphone message has an app-specific field that can store a text. The application might want
|
||||
* to use it for keeping data over restarts, like thumbnail image path.
|
||||
|
|
@ -462,35 +500,42 @@ LINPHONE_PUBLIC const char* linphone_chat_message_get_appdata(const LinphoneChat
|
|||
* @param data the data to store into the message
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_set_appdata(LinphoneChatMessage* message, const char* data);
|
||||
|
||||
/**
|
||||
* Get text part of this message
|
||||
* @return text or NULL if no text.
|
||||
*/
|
||||
LINPHONE_PUBLIC const char* linphone_chat_message_get_text(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Get the time the message was sent.
|
||||
*/
|
||||
LINPHONE_PUBLIC time_t linphone_chat_message_get_time(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* User pointer get function
|
||||
*/
|
||||
LINPHONE_PUBLIC void* linphone_chat_message_get_user_data(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
*User pointer set function
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_set_user_data(LinphoneChatMessage* message,void*);
|
||||
|
||||
/**
|
||||
* Returns the chatroom this message belongs to.
|
||||
**/
|
||||
LINPHONE_PUBLIC LinphoneChatRoom* linphone_chat_message_get_chat_room(LinphoneChatMessage *msg);
|
||||
|
||||
LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_message_get_peer_address(LinphoneChatMessage *msg);
|
||||
|
||||
/**
|
||||
* Returns the origin address of a message if it was a outgoing message, or the destination address if it was an incoming message.
|
||||
*@param message #LinphoneChatMessage obj
|
||||
*@return #LinphoneAddress
|
||||
*/
|
||||
LINPHONE_PUBLIC LinphoneAddress *linphone_chat_message_get_local_address(const LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Add custom headers to the message.
|
||||
* @param message the message
|
||||
|
|
@ -498,47 +543,56 @@ LINPHONE_PUBLIC LinphoneAddress *linphone_chat_message_get_local_address(const L
|
|||
* @param header_value header value
|
||||
**/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_add_custom_header(LinphoneChatMessage* message, const char *header_name, const char *header_value);
|
||||
|
||||
/**
|
||||
* Retrieve a custom header value given its name.
|
||||
* @param message the message
|
||||
* @param header_name header name searched
|
||||
**/
|
||||
LINPHONE_PUBLIC const char * linphone_chat_message_get_custom_header(LinphoneChatMessage* message, const char *header_name);
|
||||
|
||||
/**
|
||||
* Removes a custom header from the message.
|
||||
* @param msg the message
|
||||
* @param header_name name of the header to remove
|
||||
**/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_remove_custom_header(LinphoneChatMessage *msg, const char *header_name);
|
||||
|
||||
/**
|
||||
* Returns TRUE if the message has been read, otherwise returns FALSE.
|
||||
* @param message the message
|
||||
**/
|
||||
LINPHONE_PUBLIC bool_t linphone_chat_message_is_read(LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Returns TRUE if the message has been sent, returns FALSE if the message has been received.
|
||||
* @param message the message
|
||||
**/
|
||||
LINPHONE_PUBLIC bool_t linphone_chat_message_is_outgoing(LinphoneChatMessage* message);
|
||||
|
||||
/**
|
||||
* Returns the id used to identify this message in the storage database
|
||||
* @param message the message
|
||||
* @return the id
|
||||
*/
|
||||
LINPHONE_PUBLIC unsigned int linphone_chat_message_get_storage_id(LinphoneChatMessage* message);
|
||||
|
||||
LINPHONE_PUBLIC LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage* msg);
|
||||
|
||||
/**
|
||||
* Get full details about delivery error of a chat message.
|
||||
* @param msg a LinphoneChatMessage
|
||||
* @return a LinphoneErrorInfo describing the details.
|
||||
**/
|
||||
LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChatMessage *msg);
|
||||
|
||||
/**
|
||||
* Set the path to the file to read from or write to during the file transfer.
|
||||
* @param[in] msg LinphoneChatMessage object
|
||||
* @param[in] filepath The path to the file to use for the file transfer.
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_chat_message_set_file_transfer_filepath(LinphoneChatMessage *msg, const char *filepath);
|
||||
|
||||
/**
|
||||
* Get the path to the file to read from or write to during the file transfer.
|
||||
* @param[in] msg LinphoneChatMessage object
|
||||
|
|
|
|||
|
|
@ -3206,7 +3206,7 @@ LINPHONE_PUBLIC void linphone_core_set_root_ca_data(LinphoneCore *lc, const char
|
|||
* Set the pointer to an externally provided ssl configuration for the crypto library
|
||||
* @param lc #LinphoneCore object
|
||||
* @param[in] ssl_config A pointer to an opaque structure which will be provided directly to the crypto library used in bctoolbox. Use with extra care.
|
||||
* This ssl_config structure is responsability of the caller and will not be freed at the connection's end.
|
||||
* This ssl_config structure is responsibility of the caller and will not be freed at the connection's end.
|
||||
* @ingroup initializing
|
||||
* @endinternal
|
||||
*/
|
||||
|
|
@ -3374,7 +3374,7 @@ LINPHONE_PUBLIC const bctbx_list_t * linphone_core_get_call_logs(LinphoneCore *l
|
|||
|
||||
/**
|
||||
* Get the list of call logs (past calls) that matches the given #LinphoneAddress.
|
||||
* At the contrary of linphone_core_get_call_logs, it is your responsability to unref the logs and free this list once you are done using it.
|
||||
* At the contrary of linphone_core_get_call_logs, it is your responsibility to unref the logs and free this list once you are done using it.
|
||||
* @param[in] lc LinphoneCore object
|
||||
* @param[in] addr LinphoneAddress object
|
||||
* @return \bctbx_list{LinphoneCallLog}
|
||||
|
|
@ -4920,6 +4920,22 @@ LINPHONE_PUBLIC void linphone_core_set_im_encryption_engine(LinphoneCore *lc, Li
|
|||
*/
|
||||
LINPHONE_PUBLIC LinphoneImEncryptionEngine * linphone_core_get_im_encryption_engine(const LinphoneCore *lc);
|
||||
|
||||
/**
|
||||
* Tells whether a content type is supported.
|
||||
* @param[in] lc LinphoneCore object
|
||||
* @param[in] content_type The content type to check
|
||||
* @return A boolean value telling whether the specified content type is supported or not.
|
||||
*/
|
||||
LINPHONE_PUBLIC bool_t linphone_core_is_content_type_supported(const LinphoneCore *lc, const char *content_type);
|
||||
|
||||
/**
|
||||
* Add support for the specified content type.
|
||||
* It is the application responsibility to handle it correctly afterwards.
|
||||
* @param[in] lc LinphoneCore object
|
||||
* @param[in] content_type The content type to add support for
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_core_add_content_type_support(LinphoneCore *lc, const char *content_type);
|
||||
|
||||
#include "linphone/ringtoneplayer.h"
|
||||
#include "linphone/factory.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -307,16 +307,6 @@ typedef struct SalMessage{
|
|||
time_t time;
|
||||
}SalMessage;
|
||||
|
||||
typedef struct SalIsComposing {
|
||||
const char *from;
|
||||
const char *text;
|
||||
} SalIsComposing;
|
||||
|
||||
typedef struct SalImdn {
|
||||
const char *from;
|
||||
const char *content;
|
||||
} SalImdn;
|
||||
|
||||
#define SAL_MEDIA_DESCRIPTION_MAX_MESSAGE_ATTRIBUTES 5
|
||||
|
||||
SalMediaDescription *sal_media_description_new(void);
|
||||
|
|
@ -447,11 +437,11 @@ typedef enum SalSubscribeStatus{
|
|||
SalSubscribeTerminated
|
||||
}SalSubscribeStatus;
|
||||
|
||||
typedef enum SalTextDeliveryStatus{
|
||||
SalTextDeliveryInProgress,
|
||||
SalTextDeliveryDone,
|
||||
SalTextDeliveryFailed
|
||||
}SalTextDeliveryStatus;
|
||||
typedef enum SalMessageDeliveryStatus{
|
||||
SalMessageDeliveryInProgress,
|
||||
SalMessageDeliveryDone,
|
||||
SalMessageDeliveryFailed
|
||||
}SalMessageDeliveryStatus;
|
||||
|
||||
/**
|
||||
* auth event mode
|
||||
|
|
@ -505,10 +495,8 @@ typedef void (*SalOnRegisterFailure)(SalOp *op);
|
|||
typedef void (*SalOnVfuRequest)(SalOp *op);
|
||||
typedef void (*SalOnDtmfReceived)(SalOp *op, char dtmf);
|
||||
typedef void (*SalOnRefer)(Sal *sal, SalOp *op, const char *referto);
|
||||
typedef void (*SalOnTextReceived)(SalOp *op, const SalMessage *msg);
|
||||
typedef void (*SalOnTextDeliveryUpdate)(SalOp *op, SalTextDeliveryStatus);
|
||||
typedef void (*SalOnIsComposingReceived)(SalOp *op, const SalIsComposing *is_composing);
|
||||
typedef void (*SalOnImdnReceived)(SalOp *op, const SalImdn *imdn);
|
||||
typedef void (*SalOnMessageReceived)(SalOp *op, const SalMessage *msg);
|
||||
typedef void (*SalOnMessageDeliveryUpdate)(SalOp *op, SalMessageDeliveryStatus);
|
||||
typedef void (*SalOnNotifyRefer)(SalOp *op, SalReferStatus state);
|
||||
typedef void (*SalOnSubscribeResponse)(SalOp *op, SalSubscribeStatus status, int will_retry);
|
||||
typedef void (*SalOnNotify)(SalOp *op, SalSubscribeStatus status, const char *event, SalBodyHandler *body);
|
||||
|
|
@ -544,10 +532,8 @@ typedef struct SalCallbacks{
|
|||
SalOnVfuRequest vfu_request;
|
||||
SalOnDtmfReceived dtmf_received;
|
||||
SalOnRefer refer_received;
|
||||
SalOnTextReceived text_received;
|
||||
SalOnTextDeliveryUpdate text_delivery_update;
|
||||
SalOnIsComposingReceived is_composing_received;
|
||||
SalOnImdnReceived imdn_received;
|
||||
SalOnMessageReceived message_received;
|
||||
SalOnMessageDeliveryUpdate message_delivery_update;
|
||||
SalOnNotifyRefer notify_refer;
|
||||
SalOnSubscribeReceived subscribe_received;
|
||||
SalOnIncomingSubscribeClosed incoming_subscribe_closed;
|
||||
|
|
@ -644,6 +630,8 @@ void sal_use_tcp_tls_keepalive(Sal *ctx, bool_t enabled);
|
|||
int sal_set_tunnel(Sal *ctx, void *tunnelclient);
|
||||
/*Default value is true*/
|
||||
void sal_enable_sip_update_method(Sal *ctx,bool_t value);
|
||||
bool_t sal_is_content_type_supported(const Sal *sal, const char *content_type);
|
||||
void sal_add_content_type_support(Sal *sal, const char *content_type);
|
||||
|
||||
/**
|
||||
* returns keepalive period in ms
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
[Dolphin]
|
||||
PreviewsShown=true
|
||||
Timestamp=2015,8,3,13,7,36
|
||||
Version=3
|
||||
|
|
@ -130,9 +130,11 @@ set(RC_FILES
|
|||
rcfiles/zero_length_params_rc
|
||||
)
|
||||
|
||||
set(IMAGE_FILES
|
||||
set(IMAGE_FILES
|
||||
images/linphone.svg
|
||||
images/nowebcamCIF.jpg
|
||||
images/nowebcamVGA.jpg)
|
||||
images/nowebcamVGA.jpg
|
||||
)
|
||||
|
||||
set(VCARD_FILES
|
||||
vcards/thousand_vcards.vcf
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ RCFILES = \
|
|||
rcfiles/friends_rc\
|
||||
rcfiles/carddav_rc
|
||||
|
||||
IMAGE_FILES = images/nowebcamCIF.jpg images/nowebcamVGA.jpg
|
||||
IMAGE_FILES = images/linphone.svg images/nowebcamCIF.jpg images/nowebcamVGA.jpg
|
||||
|
||||
VCARDS_FILE = vcards/vcards.vcf vcards/thousand_vcards.vcf
|
||||
|
||||
|
|
|
|||
83
tester/images/linphone.svg
Normal file
83
tester/images/linphone.svg
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Calque_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#C95328" d="M512,433.229C512,476.562,476.557,512,433.228,512H78.768C35.452,512,0,476.562,0,433.229V78.763
|
||||
C0,35.43,35.452,0,78.768,0h354.46C476.557,0,512,35.43,512,78.763V433.229z"/>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="256.0044" y1="498.877" x2="256.0044" y2="13.1323">
|
||||
<stop offset="0" style="stop-color:#E6B043"/>
|
||||
<stop offset="0.5" style="stop-color:#C95328"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M78.768,498.877c-36.188,0-65.632-29.438-65.632-65.648V78.763
|
||||
c0-36.21,29.443-65.631,65.632-65.631h354.46c36.202,0,65.645,29.421,65.645,65.631v354.465c0,36.21-29.443,65.648-65.645,65.648
|
||||
H78.768z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#C95328" d="M473.412,129.19c4.022,16.057,11.006,21.111,14.433,6.539c2.626-12.988-0.907-31.173-15.294-50.564
|
||||
c-28.484-41.915-103.78-66.186-162.935-44.793c-31.137,11.984-24.661,13.922,7.499,7.175
|
||||
c58.118-10.362,116.214,15.009,141.418,51.351C466.034,109.169,470.832,119.557,473.412,129.19z"/>
|
||||
<path fill="#C95328" d="M419.015,143.098c7.494,14.941,12.717,26.531,19.203,25.729c6.259-0.803,15.077-12.937,11.313-29.547
|
||||
c-2.509-10.809-8.09-22.524-17.543-33.749c-28.203-35.815-93.65-54.292-150.259-31.286
|
||||
c-29.453,11.077-49.386,32.979-50.211,36.716c-1.109,4.036,18.227-9.882,45.146-18.015
|
||||
c35.711-11.303,76.217-6.516,105.15,11.224C400.18,115.171,412.271,129.452,419.015,143.098z"/>
|
||||
<path fill="#C95328" d="M272.768,116.451c-23.938,6.501-41.564,24.912-39.126,28.578c2.162,3.725,15.671-0.811,29.184-4.844
|
||||
c8.038-2.487,16.081-3.953,23.938-4.465c25.749-1.574,45.468,6.943,57.738,17.625c11.416,9.433,22.448,19.878,31.148,19.694
|
||||
c8.566-0.117,15.136-11.611,7.022-24.39c-8.806-13.192-24.526-26.198-47.588-32.772
|
||||
C316.077,110.415,294.023,110.359,272.768,116.451z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M471.9,135.938c4.029,16.063,11.007,21.124,14.451,6.533c2.614-12.977-0.917-31.161-15.322-50.563
|
||||
c-28.482-41.924-103.761-66.172-162.917-44.792c-31.146,11.995-24.653,13.946,7.502,7.184
|
||||
c58.08-10.377,116.211,14.988,141.407,51.347C464.529,115.924,469.323,126.286,471.9,135.938z"/>
|
||||
<path fill="#FFFFFF" d="M417.504,149.867c7.505,14.928,12.724,26.518,19.188,25.708c6.278-0.797,15.089-12.928,11.332-29.564
|
||||
c-2.513-10.809-8.095-22.504-17.548-33.728C402.261,76.469,336.838,58,280.209,81.002
|
||||
c-29.433,11.073-49.345,32.962-50.192,36.706c-1.089,4.03,18.224-9.864,45.125-17.983c35.73-11.315,76.235-6.557,105.167,11.199
|
||||
C398.668,121.933,410.762,136.223,417.504,149.867z"/>
|
||||
<path fill="#FFFFFF" d="M271.267,123.201c-23.948,6.485-41.572,24.917-39.134,28.582c2.168,3.729,15.69-0.8,29.175-4.826
|
||||
c8.052-2.511,16.09-3.969,23.948-4.463c25.726-1.586,45.473,6.911,57.743,17.604c11.416,9.432,22.457,19.879,31.142,19.694
|
||||
c8.571-0.115,15.134-11.604,7.032-24.405c-8.817-13.18-24.553-26.163-47.588-32.739
|
||||
C314.582,117.183,292.515,117.094,271.267,123.201z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path fill="none" d="M78.768,499.246c-36.188,0-65.632-29.439-65.632-65.649V79.132c0-36.211,29.443-65.632,65.632-65.632h354.46
|
||||
c36.202,0,65.645,29.421,65.645,65.632v354.465c0,36.21-29.443,65.649-65.645,65.649H78.768z"/>
|
||||
<g>
|
||||
<path fill="#C95328" d="M137.141,473.474c29.627,7.798,61.013,10.667,86.881,4.377c48.737-11.859,64.443-28.113,61.93-68.991
|
||||
c0,0,0.908-19.719-40.68-18.982c-9.21,0.167-32.732,5.088-36.07,15.193c-3.342,10.114,1.719,20.368,1.991,25.324
|
||||
s-13.443,11.676-19.206,12.246c-7.496,0.736-26.381-0.456-45.438-5.158l-0.82-0.211c-18.89-5.324-35.903-13.613-42.053-17.955
|
||||
c-4.732-3.343-13.342-15.948-10.658-20.132c2.684-4.176,12.149-10.597,14.237-21.035c2.088-10.438-15.934-26.333-23.864-31.026
|
||||
c-35.798-21.185-44.75-3.579-44.75-3.579c-22.373,34.308-16.737,56.193,19.789,90.57c19.386,18.245,48.101,31.245,77.711,39.097
|
||||
L137.141,473.474z"/>
|
||||
<path fill="#FFFFFF" d="M135.768,478.956c29.632,7.799,61.018,10.667,86.882,4.368c48.741-11.851,64.447-28.104,61.93-68.982
|
||||
c0,0,0.912-19.728-40.676-18.982c-9.215,0.167-32.732,5.079-36.074,15.193c-3.338,10.114,1.719,20.368,1.996,25.324
|
||||
c0.272,4.956-13.443,11.676-19.21,12.246c-7.491,0.736-26.382-0.456-45.435-5.158l-0.82-0.21
|
||||
c-18.891-5.325-35.904-13.614-42.057-17.956c-4.728-3.343-13.342-15.957-10.658-20.132c2.689-4.176,12.149-10.597,14.241-21.035
|
||||
c2.088-10.447-15.938-26.333-23.868-31.026c-35.794-21.185-44.746-3.588-44.746-3.588c-22.373,34.307-16.737,56.193,19.79,90.579
|
||||
c19.386,18.236,48.097,31.245,77.71,39.097L135.768,478.956z"/>
|
||||
</g>
|
||||
<path fill="#C95328" d="M301.623,271.947l29.996-80.491l-103.663,42.298c-2.732-0.666-5.474-1.368-7.509-2.052
|
||||
c-26.605-8.176-51.837-11.605-76.395-12.264c-32.062,0.658-61.403,5.438-87.325,15c-16.438,6.843-30.881,15.378-43.658,25.491
|
||||
v45.518c2.197-2.176,4.412-4.324,6.815-6.201c12.97-10.264,26.605-17.729,40.943-23.211c14.329-4.771,27.969-8.167,40.921-10.229
|
||||
c12.29-1.351,21.838-2.719,27.978-2.719c22.513,0,45.026,3.412,66.833,9.544c22.513,6.149,43.675,15.685,62.754,27.947
|
||||
c18.443,12.982,34.127,28.667,45.724,47.745c6.833,11.614,11.596,21.869,15,32.79c3.408,10.22,5.447,20.456,5.447,30
|
||||
c-0.667,19.097-6.811,37.526-19.096,53.877c-9.505,14.026-23.207,25.22-40.601,34.447h51.587
|
||||
c7.053-5.859,14.189-12.649,21.759-20.78c8.189-9.535,15.698-21.132,21.163-34.816c6.123-13.631,9.54-28.64,9.54-44.99
|
||||
c-0.697-28.658-7.509-54.605-21.847-77.088C335.719,302.632,320.04,285.605,301.623,271.947z"/>
|
||||
<path fill="#FFFFFF" d="M299.688,280.614l30.004-80.518l-103.662,42.289c-2.732-0.675-5.474-1.351-7.509-2.044
|
||||
c-26.605-8.184-51.833-11.604-76.399-12.254c-32.066,0.649-61.399,5.438-87.32,14.982c-15.759,6.579-29.645,14.729-42.035,24.281
|
||||
v45.245c1.71-1.605,3.36-3.298,5.188-4.72c12.974-10.254,26.605-17.71,40.938-23.184c14.338-4.781,27.974-8.185,40.926-10.255
|
||||
c12.29-1.333,21.838-2.719,27.978-2.719c22.518,0,45.026,3.43,66.829,9.544c22.513,6.157,43.684,15.701,62.763,27.982
|
||||
c18.438,12.947,34.127,28.648,45.715,47.728c6.824,11.614,11.605,21.833,15.009,32.78c3.408,10.211,5.447,20.457,5.447,29.992
|
||||
c-0.667,19.113-6.816,37.535-19.097,53.912c-6.623,9.754-15.324,18.131-25.842,25.456h46.868
|
||||
c3.825-3.632,7.702-7.509,11.719-11.825c8.189-9.518,15.693-21.131,21.158-34.78c6.118-13.649,9.543-28.658,9.543-45.009
|
||||
c-0.697-28.693-7.509-54.605-21.851-77.088C333.794,311.281,318.114,294.237,299.688,280.614z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
|
|
@ -279,6 +279,10 @@ void text_message_base(LinphoneCoreManager* marie, LinphoneCoreManager* pauline)
|
|||
|
||||
BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneMessageDelivered,1));
|
||||
BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceived,1));
|
||||
BC_ASSERT_PTR_NOT_NULL(marie->stat.last_received_chat_message);
|
||||
if (marie->stat.last_received_chat_message != NULL) {
|
||||
BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(marie->stat.last_received_chat_message), "text/plain");
|
||||
}
|
||||
|
||||
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_chat_room(marie->lc,pauline->identity));
|
||||
}
|
||||
|
|
@ -1410,6 +1414,7 @@ static void lime_unit(void) {
|
|||
xmlDocPtr cacheBufferBob;
|
||||
uint8_t *multipartMessage = NULL;
|
||||
uint8_t *decryptedMessage = NULL;
|
||||
char *decryptedContentType = NULL;
|
||||
xmlChar *xmlStringOutput;
|
||||
int xmlStringLength;
|
||||
limeURIKeys_t associatedKeys;
|
||||
|
|
@ -1529,7 +1534,7 @@ static void lime_unit(void) {
|
|||
|
||||
|
||||
/* encrypt a msg */
|
||||
retval = lime_createMultipartMessage(cacheBufferAlice, (uint8_t *)PLAIN_TEXT_TEST_MESSAGE, (uint8_t *)"sip:pauline@sip.example.org", &multipartMessage);
|
||||
retval = lime_createMultipartMessage(cacheBufferAlice, "text/plain", (uint8_t *)PLAIN_TEXT_TEST_MESSAGE, (uint8_t *)"sip:pauline@sip.example.org", &multipartMessage);
|
||||
|
||||
BC_ASSERT_EQUAL(retval, 0, int, "%d");
|
||||
if (retval == 0) {
|
||||
|
|
@ -1537,15 +1542,17 @@ static void lime_unit(void) {
|
|||
}
|
||||
|
||||
/* decrypt the multipart msg */
|
||||
retval = lime_decryptMultipartMessage(cacheBufferBob, multipartMessage, &decryptedMessage);
|
||||
retval = lime_decryptMultipartMessage(cacheBufferBob, multipartMessage, &decryptedMessage, &decryptedContentType);
|
||||
|
||||
BC_ASSERT_EQUAL(retval, 0, int, "%d");
|
||||
if (retval == 0) {
|
||||
BC_ASSERT_STRING_EQUAL((char *)decryptedMessage, (char *)PLAIN_TEXT_TEST_MESSAGE);
|
||||
BC_ASSERT_STRING_EQUAL((char *)decryptedContentType, "text/plain");
|
||||
ms_message("Succesfully decrypted msg is %s", decryptedMessage);
|
||||
}
|
||||
free(multipartMessage);
|
||||
free(decryptedMessage);
|
||||
ms_free(multipartMessage);
|
||||
ms_free(decryptedMessage);
|
||||
ms_free(decryptedContentType);
|
||||
|
||||
/* update ZID files */
|
||||
/* dump the xml document into a string */
|
||||
|
|
@ -2304,6 +2311,64 @@ void chat_message_custom_headers(void) {
|
|||
linphone_core_manager_destroy(pauline);
|
||||
}
|
||||
|
||||
void _text_message_with_custom_content_type(bool_t with_lime) {
|
||||
LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
|
||||
LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
|
||||
LinphoneChatRoom *chat_room = linphone_core_get_chat_room(pauline->lc, marie->identity);
|
||||
LinphoneChatMessage *msg;
|
||||
LinphoneChatMessageCbs *cbs;
|
||||
bctbx_vfs_t *vfs = bctbx_vfs_get_default();
|
||||
char *send_filepath;
|
||||
bctbx_vfs_file_t *file_to_send;
|
||||
size_t file_size;
|
||||
char *buf;
|
||||
|
||||
if (with_lime) {
|
||||
if (enable_lime_for_message_test(marie, pauline) < 0) goto end;
|
||||
}
|
||||
|
||||
send_filepath = bc_tester_res("images/linphone.svg");
|
||||
file_to_send = bctbx_file_open(vfs, send_filepath, "r");
|
||||
file_size = (size_t)bctbx_file_size(file_to_send);
|
||||
buf = bctbx_malloc(file_size + 1);
|
||||
bctbx_file_read(file_to_send, buf, file_size, 0);
|
||||
buf[file_size] = '\0';
|
||||
bctbx_file_close(file_to_send);
|
||||
bc_free(send_filepath);
|
||||
msg = linphone_chat_room_create_message(chat_room, buf);
|
||||
linphone_chat_message_set_content_type(msg, "image/svg+xml");
|
||||
|
||||
linphone_core_add_content_type_support(marie->lc, "image/svg+xml");
|
||||
linphone_core_add_content_type_support(pauline->lc, "image/svg+xml");
|
||||
cbs = linphone_chat_message_get_callbacks(msg);
|
||||
linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed);
|
||||
linphone_chat_room_send_chat_message(chat_room, msg);
|
||||
|
||||
BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneMessageReceived, 1));
|
||||
BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneMessageDelivered, 1));
|
||||
|
||||
if (marie->stat.last_received_chat_message) {
|
||||
BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(marie->stat.last_received_chat_message), "image/svg+xml");
|
||||
BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(marie->stat.last_received_chat_message), buf);
|
||||
}
|
||||
|
||||
bctbx_free(buf);
|
||||
|
||||
end:
|
||||
linphone_core_manager_destroy(marie);
|
||||
linphone_core_manager_destroy(pauline);
|
||||
remove("tmpZIDCacheMarie.xml");
|
||||
remove("tmpZIDCachePauline.xml");
|
||||
}
|
||||
|
||||
void text_message_with_custom_content_type(void) {
|
||||
_text_message_with_custom_content_type(FALSE);
|
||||
}
|
||||
|
||||
void text_message_with_custom_content_type_and_lime(void) {
|
||||
_text_message_with_custom_content_type(TRUE);
|
||||
}
|
||||
|
||||
test_t message_tests[] = {
|
||||
TEST_NO_TAG("Text message", text_message),
|
||||
TEST_NO_TAG("Text message within call dialog", text_message_within_call_dialog),
|
||||
|
|
@ -2371,7 +2436,9 @@ test_t message_tests[] = {
|
|||
TEST_ONE_TAG("Real Time Text offer answer with different payload numbers (sender side)", real_time_text_message_different_text_codecs_payload_numbers_sender_side, "RTT"),
|
||||
TEST_ONE_TAG("Real Time Text offer answer with different payload numbers (receiver side)", real_time_text_message_different_text_codecs_payload_numbers_receiver_side, "RTT"),
|
||||
TEST_ONE_TAG("Real Time Text copy paste", real_time_text_copy_paste, "RTT"),
|
||||
TEST_NO_TAG("IM Encryption Engine custom headers", chat_message_custom_headers)
|
||||
TEST_NO_TAG("IM Encryption Engine custom headers", chat_message_custom_headers),
|
||||
TEST_NO_TAG("Text message with custom content-type", text_message_with_custom_content_type),
|
||||
TEST_ONE_TAG("Text message with custom content-type and lime", text_message_with_custom_content_type_and_lime, "LIME")
|
||||
};
|
||||
|
||||
test_suite_t message_test_suite = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue