mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-30 09:49:26 +00:00
File transfer implemented following RCS5.1 recommendation
- memory leaks to be fixed
This commit is contained in:
parent
49e8c4f93a
commit
c10b5f652b
10 changed files with 405 additions and 32 deletions
|
|
@ -55,6 +55,11 @@ static void process_response_event(void *op_base, const belle_sip_response_event
|
|||
op->base.root->callbacks.text_delivery_update(op,status);
|
||||
}
|
||||
|
||||
static bool_t is_rcs_filetransfer(belle_sip_header_content_type_t* content_type) {
|
||||
return strcmp("application",belle_sip_header_content_type_get_type(content_type))==0
|
||||
&& strcmp("vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0;
|
||||
}
|
||||
|
||||
static bool_t is_plain_text(belle_sip_header_content_type_t* content_type) {
|
||||
return strcmp("text",belle_sip_header_content_type_get_type(content_type))==0
|
||||
&& strcmp("plain",belle_sip_header_content_type_get_subtype(content_type))==0;
|
||||
|
|
@ -69,7 +74,7 @@ static bool_t is_im_iscomposing(belle_sip_header_content_type_t* content_type) {
|
|||
}
|
||||
|
||||
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"));
|
||||
belle_sip_message_add_header(msg,belle_sip_header_create("Accept","text/plain, message/external-body, application/im-iscomposing+xml, application/vnd.gsma.rcs-ft-http+xml"));
|
||||
}
|
||||
|
||||
void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *event){
|
||||
|
|
@ -85,11 +90,13 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
|
|||
char* from;
|
||||
bool_t plain_text=FALSE;
|
||||
bool_t external_body=FALSE;
|
||||
bool_t rcs_filetransfer=FALSE;
|
||||
|
||||
from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t);
|
||||
content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t);
|
||||
if (content_type && ((plain_text=is_plain_text(content_type))
|
||||
|| (external_body=is_external_body(content_type)))) {
|
||||
|| (external_body=is_external_body(content_type))
|
||||
|| (rcs_filetransfer=is_rcs_filetransfer(content_type)))) {
|
||||
SalMessage salmsg;
|
||||
char message_id[256]={0};
|
||||
|
||||
|
|
@ -104,8 +111,12 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
|
|||
,belle_sip_header_call_id_get_call_id(call_id)
|
||||
,belle_sip_header_cseq_get_seq_number(cseq));
|
||||
salmsg.from=from;
|
||||
salmsg.text=plain_text?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
|
||||
salmsg.text=(plain_text||rcs_filetransfer)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
|
||||
salmsg.url=NULL;
|
||||
salmsg.content_type = NULL;
|
||||
if (rcs_filetransfer) { /* if we have a rcs file transfer, set the type, message body (stored in salmsg.text) contains all needed information to retrieve the file */
|
||||
salmsg.content_type = "application/vnd.gsma.rcs-ft-http+xml";
|
||||
}
|
||||
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 "*/
|
||||
|
|
|
|||
326
coreapi/chat.c
326
coreapi/chat.c
|
|
@ -25,6 +25,7 @@
|
|||
#include "linphonecore.h"
|
||||
#include "private.h"
|
||||
#include "lpconfig.h"
|
||||
#include "belle-sip/belle-sip.h"
|
||||
|
||||
#include <libxml/xmlwriter.h>
|
||||
|
||||
|
|
@ -32,6 +33,135 @@
|
|||
#define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60
|
||||
#define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120
|
||||
|
||||
|
||||
static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage* msg);
|
||||
#define MULTIPART_BOUNDARY "---------------------------14737809831466499882746641449"
|
||||
#define MULTIPART_HEADER_1 "--" MULTIPART_BOUNDARY "\r\n" \
|
||||
"Content-Disposition: form-data; name=\"File\"; filename=\""
|
||||
#define MULTIPART_HEADER_2 "\"\r\n" \
|
||||
"Content-Type: "
|
||||
#define MULTIPART_HEADER_3 "\r\n\r\n"
|
||||
#define MULTIPART_END "\r\n--" MULTIPART_BOUNDARY "--\r\n"
|
||||
const char *multipart_boundary=MULTIPART_BOUNDARY;
|
||||
|
||||
static size_t linphone_chat_message_compute_multipart_header_size(const char *filename, const char *content_type) {
|
||||
return strlen(MULTIPART_HEADER_1)+strlen(filename)+strlen(MULTIPART_HEADER_2)+strlen(content_type)+strlen(MULTIPART_HEADER_3);
|
||||
}
|
||||
static void process_io_error(void *data, const belle_sip_io_error_event_t *event){
|
||||
printf("We have a response io error!\n");
|
||||
}
|
||||
static void process_auth_requested(void *data, belle_sip_auth_event_t *event){
|
||||
printf("We have a auth requested!\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback called during upload or download of a file from server
|
||||
* It is just forwarding the call and some parameters to the vtable defined callback
|
||||
*/
|
||||
static void linphone_chat_message_file_transfer_on_progress(belle_sip_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, size_t total){
|
||||
LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
|
||||
LinphoneCore *lc = chatMsg->chat_room->lc;
|
||||
/* call back given by application level */
|
||||
if (lc->vtable.file_transfer_progress_indication != NULL) {
|
||||
lc->vtable.file_transfer_progress_indication(lc, chatMsg, chatMsg->file_transfer_information, (size_t)(((double)offset/(double)total)*100.0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback called when posting a file to server (following rcs5.1 recommendation)
|
||||
*
|
||||
* @param bh the body handler
|
||||
* @param msg the belle sip message
|
||||
* @param data the user data associated to the handler, contains the linphoneChatMessage we're working on
|
||||
* @param offset current position in the input buffer
|
||||
* @param buffer the ouput buffer we to copy the data to be uploaded
|
||||
* @param size size in byte of the data requested, as output it will contain the effective copied size
|
||||
*
|
||||
*/
|
||||
static int linphone_chat_message_file_transfer_on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, void *buffer, size_t *size){
|
||||
LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
|
||||
LinphoneCore *lc = chatMsg->chat_room->lc;
|
||||
|
||||
char *content_type=belle_sip_strdup_printf("%s/%s", chatMsg->file_transfer_information->type, chatMsg->file_transfer_information->subtype);
|
||||
size_t end_of_file=linphone_chat_message_compute_multipart_header_size(chatMsg->file_transfer_information->name, content_type)+chatMsg->file_transfer_information->size;
|
||||
|
||||
if (offset==0){
|
||||
int partlen=linphone_chat_message_compute_multipart_header_size(chatMsg->file_transfer_information->name, content_type);
|
||||
memcpy(buffer,MULTIPART_HEADER_1,strlen(MULTIPART_HEADER_1));
|
||||
buffer += strlen(MULTIPART_HEADER_1);
|
||||
memcpy(buffer,chatMsg->file_transfer_information->name,strlen(chatMsg->file_transfer_information->name));
|
||||
buffer += strlen(chatMsg->file_transfer_information->name);
|
||||
memcpy(buffer,MULTIPART_HEADER_2,strlen(MULTIPART_HEADER_2));
|
||||
buffer += strlen(MULTIPART_HEADER_2);
|
||||
memcpy(buffer,content_type,strlen(content_type));
|
||||
buffer += strlen(content_type);
|
||||
memcpy(buffer,MULTIPART_HEADER_3,strlen(MULTIPART_HEADER_3));
|
||||
|
||||
*size=partlen;
|
||||
}else if (offset<end_of_file){
|
||||
/* get data from call back */
|
||||
lc->vtable.file_transfer_send(lc, chatMsg, chatMsg->file_transfer_information, buffer, size);
|
||||
}else{
|
||||
*size=strlen(MULTIPART_END);
|
||||
strncpy(buffer,MULTIPART_END,*size);
|
||||
}
|
||||
belle_sip_free(content_type);
|
||||
return BELLE_SIP_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function called when we have a response from server during a file upload to server (rcs5.1 recommandation)
|
||||
* Note: The first post is empty and the server shall reply a 204 (No content) message, this will trigger a new post request to the server
|
||||
* to upoad the file. The server response to this second post is processed by this same function
|
||||
*
|
||||
* @param data the user define pointer associated with the request, it contains the linphoneChatMessage we're trying to send
|
||||
* @param event the response from server
|
||||
*/
|
||||
static void linphone_chat_message_process_response_from_post_file(void *data, const belle_http_response_event_t *event){
|
||||
LinphoneChatMessage* msg=(LinphoneChatMessage *)data;
|
||||
|
||||
/* check the answer code */
|
||||
if (event->response){
|
||||
int code=belle_http_response_get_status_code(event->response);
|
||||
if (code == 204) { /* this is the reply to the first post to the server - an empty message */
|
||||
/* start uploading the file */
|
||||
belle_http_request_listener_callbacks_t cbs={0};
|
||||
belle_http_request_listener_t *l;
|
||||
belle_generic_uri_t *uri;
|
||||
belle_http_request_t *req;
|
||||
char *content_type=belle_sip_strdup_printf("%s/%s", msg->file_transfer_information->type, msg->file_transfer_information->subtype);
|
||||
belle_sip_user_body_handler_t *bh=belle_sip_user_body_handler_new(msg->file_transfer_information->size+linphone_chat_message_compute_multipart_header_size(msg->file_transfer_information->name, content_type)+strlen(MULTIPART_END), linphone_chat_message_file_transfer_on_progress, NULL, linphone_chat_message_file_transfer_on_send_body, msg);
|
||||
belle_sip_free(content_type);
|
||||
content_type=belle_sip_strdup_printf("multipart/form-data; boundary=%s",multipart_boundary);
|
||||
|
||||
uri=belle_generic_uri_parse(msg->chat_room->lc->file_transfer_server);
|
||||
|
||||
req=belle_http_request_create("POST",
|
||||
uri,
|
||||
belle_sip_header_create("User-Agent","belle-sip/" PACKAGE_VERSION),
|
||||
belle_sip_header_create("Content-type",content_type),
|
||||
NULL);
|
||||
belle_sip_free(content_type);
|
||||
belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req),BELLE_SIP_BODY_HANDLER(bh));
|
||||
cbs.process_response=linphone_chat_message_process_response_from_post_file;
|
||||
cbs.process_io_error=process_io_error;
|
||||
cbs.process_auth_requested=process_auth_requested;
|
||||
l=belle_http_request_listener_create_from_callbacks(&cbs,msg);
|
||||
belle_http_provider_send_request(msg->chat_room->lc->http_provider,req,l);
|
||||
}
|
||||
if (code == 200 ) { /* file has been uplaoded correctly, get server reply and send it */
|
||||
const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response);
|
||||
msg->message = ms_strdup(body);
|
||||
msg->file_transfer_information = NULL;
|
||||
msg->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml");
|
||||
_linphone_chat_room_send_message(msg->chat_room, msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void _linphone_chat_message_destroy(LinphoneChatMessage* msg);
|
||||
|
||||
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessage);
|
||||
|
|
@ -178,6 +308,30 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM
|
|||
const char *identity=NULL;
|
||||
time_t t=time(NULL);
|
||||
|
||||
/* Check if we shall upload a file to a server */
|
||||
if (msg->file_transfer_information != NULL) {
|
||||
/* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */
|
||||
belle_http_request_listener_callbacks_t cbs={0};
|
||||
belle_http_request_listener_t *l;
|
||||
belle_generic_uri_t *uri;
|
||||
belle_http_request_t *req;
|
||||
|
||||
uri=belle_generic_uri_parse(cr->lc->file_transfer_server);
|
||||
|
||||
req=belle_http_request_create("POST",
|
||||
uri,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
cbs.process_response=linphone_chat_message_process_response_from_post_file;
|
||||
cbs.process_io_error=process_io_error;
|
||||
cbs.process_auth_requested=process_auth_requested;
|
||||
l=belle_http_request_listener_create_from_callbacks(&cbs,msg); /* give msg to listener to be able to start the actual file upload when server answer a 204 No content */
|
||||
belle_http_provider_send_request(cr->lc->http_provider,req,l);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (lp_config_get_int(cr->lc->config,"sip","chat_use_call_dialogs",0)){
|
||||
if((call = linphone_core_get_call_by_remote_address(cr->lc,cr->peer))!=NULL){
|
||||
if (call->state==LinphoneCallConnected ||
|
||||
|
|
@ -207,7 +361,11 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM
|
|||
sal_message_send(op,identity,cr->peer,content_type, NULL);
|
||||
ms_free(content_type);
|
||||
} else {
|
||||
sal_text_send(op, identity, cr->peer,msg->message);
|
||||
if (msg->content_type == NULL) {
|
||||
sal_text_send(op, identity, cr->peer,msg->message);
|
||||
} else {
|
||||
sal_message_send(op, identity, cr->peer, msg->content_type, msg->message);
|
||||
}
|
||||
}
|
||||
msg->dir=LinphoneChatMessageOutgoing;
|
||||
msg->from=linphone_address_new(identity);
|
||||
|
|
@ -280,7 +438,65 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
|
|||
/* create a new chat room */
|
||||
cr=linphone_core_create_chat_room(lc,cleanfrom);
|
||||
}
|
||||
msg = linphone_chat_room_create_message(cr, sal_msg->text);
|
||||
if (sal_msg->content_type != NULL) { /* content_type field is, for now, used only for rcs file transfer bu twe shall strcmp it with "application/vnd.gsma.rcs-ft-http+xml" */
|
||||
msg = linphone_chat_room_create_message(cr, NULL); /* create a message with empty body */
|
||||
msg->content_type = ms_strdup(sal_msg->content_type); /* add the content_type "application/vnd.gsma.rcs-ft-http+xml" */
|
||||
msg->file_transfer_information = (LinphoneContent *)malloc(sizeof(LinphoneContent));
|
||||
memset(msg->file_transfer_information, 0, sizeof(*(msg->file_transfer_information)));
|
||||
|
||||
xmlChar *file_url = NULL;
|
||||
/* parse the message body to get all informations from it */
|
||||
xmlDocPtr xmlMessageBody = xmlParseDoc((const xmlChar *)sal_msg->text);
|
||||
|
||||
xmlNodePtr 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);
|
||||
msg->file_transfer_information->size = strtol((const char*)fileSizeString, NULL, 10);
|
||||
xmlFree(fileSizeString);
|
||||
}
|
||||
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-name")) {
|
||||
msg->file_transfer_information->name = (char *)xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
}
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"content-type")) {
|
||||
xmlChar *contentType = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
|
||||
int contentTypeIndex = 0;
|
||||
while (contentType[contentTypeIndex]!='/' && contentType[contentTypeIndex]!='\0') {
|
||||
contentTypeIndex++;
|
||||
}
|
||||
msg->file_transfer_information->type = strndup((char *)contentType, contentTypeIndex);
|
||||
msg->file_transfer_information->subtype = strdup(((char *)contentType+contentTypeIndex+1));
|
||||
xmlFree(contentType);
|
||||
}
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) {
|
||||
file_url = xmlGetProp(cur, (const xmlChar *)"url");
|
||||
}
|
||||
|
||||
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);
|
||||
} else { /* message is not rcs file transfer, create it with provided sal_msg->text as ->message */
|
||||
msg = linphone_chat_room_create_message(cr, sal_msg->text);
|
||||
}
|
||||
linphone_chat_message_set_from(msg, cr->peer_url);
|
||||
|
||||
{
|
||||
|
|
@ -299,6 +515,7 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
|
|||
if (sal_msg->url) {
|
||||
linphone_chat_message_set_external_body_url(msg, sal_msg->url);
|
||||
}
|
||||
|
||||
linphone_address_destroy(addr);
|
||||
msg->storage_id=linphone_chat_message_store(msg);
|
||||
linphone_chat_room_message_received(cr,lc,msg);
|
||||
|
|
@ -426,6 +643,7 @@ LinphoneChatMessage* linphone_chat_room_create_message(LinphoneChatRoom *cr, con
|
|||
msg->chat_room=(LinphoneChatRoom*)cr;
|
||||
msg->message=message?ms_strdup(message):NULL;
|
||||
msg->is_read=TRUE;
|
||||
msg->content_type = NULL; /* this property is used only when transfering file */
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
|
@ -452,6 +670,7 @@ LinphoneChatMessage* linphone_chat_room_create_message_2(
|
|||
msg->time=time;
|
||||
msg->state=state;
|
||||
msg->is_read=is_read;
|
||||
msg->content_type = NULL; /* this property is used only when transfering file */
|
||||
if (is_incoming) {
|
||||
msg->dir=LinphoneChatMessageIncoming;
|
||||
linphone_chat_message_set_from(msg, linphone_chat_room_get_peer_address(cr));
|
||||
|
|
@ -668,6 +887,87 @@ void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message,co
|
|||
message->external_body_url=url?ms_strdup(url):NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file_transfer_information (used by call backs to recover informations during a rcs file transfer)
|
||||
*
|
||||
* @param message #LinphoneChatMessage
|
||||
* @return a pointer to the LinphoneContent structure or NULL if not present.
|
||||
*/
|
||||
const LinphoneContent *linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage*message) {
|
||||
return message->file_transfer_information;
|
||||
}
|
||||
|
||||
static void on_recv_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, const void *buffer, size_t size){
|
||||
//printf("Receive %ld bytes\n\n%s\n\n", size, (char *)buffer);
|
||||
LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
|
||||
LinphoneCore *lc = chatMsg->chat_room->lc;
|
||||
/* call back given by application level */
|
||||
if (lc->vtable.file_transfer_received != NULL) {
|
||||
lc->vtable.file_transfer_received(lc, chatMsg, chatMsg->file_transfer_information, buffer, size);
|
||||
}
|
||||
return;
|
||||
|
||||
/* feed the callback with the received data */
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void linphone_chat_process_response_headers_from_get_file(void *data, const belle_http_response_event_t *event){
|
||||
if (event->response){
|
||||
/*we are receiving a response, set a specific body handler to acquire the response.
|
||||
* if not done, belle-sip will create a memory body handler, the default*/
|
||||
LinphoneChatMessage *message=belle_sip_object_data_get(BELLE_SIP_OBJECT(event->request),"message");
|
||||
belle_sip_message_set_body_handler(
|
||||
(belle_sip_message_t*)event->response,
|
||||
(belle_sip_body_handler_t*)belle_sip_user_body_handler_new(message->file_transfer_information->size, linphone_chat_message_file_transfer_on_progress,on_recv_body,NULL,message)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void linphone_chat_process_response_from_get_file(void *data, const belle_http_response_event_t *event){
|
||||
//LinphoneChatMessage* msg=(LinphoneChatMessage *)data;
|
||||
|
||||
/* check the answer code */
|
||||
if (event->response){
|
||||
int code=belle_http_response_get_status_code(event->response);
|
||||
if (code==200) {
|
||||
LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
|
||||
LinphoneCore *lc = chatMsg->chat_room->lc;
|
||||
/* file downloaded succesfully, call again the callback with size at zero */
|
||||
if (lc->vtable.file_transfer_received != NULL) {
|
||||
lc->vtable.file_transfer_received(lc, chatMsg, chatMsg->file_transfer_information, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the download of the file from remote server
|
||||
*
|
||||
* @param message #LinphoneChatMessage
|
||||
*/
|
||||
void linphone_chat_message_start_file_download(const LinphoneChatMessage *message) {
|
||||
belle_http_request_listener_callbacks_t cbs={0};
|
||||
belle_http_request_listener_t *l;
|
||||
belle_generic_uri_t *uri;
|
||||
belle_http_request_t *req;
|
||||
const char *url=message->external_body_url;
|
||||
|
||||
uri=belle_generic_uri_parse(url);
|
||||
|
||||
req=belle_http_request_create("GET",
|
||||
uri,
|
||||
belle_sip_header_create("User-Agent","belle-sip/" PACKAGE_VERSION),
|
||||
NULL);
|
||||
|
||||
cbs.process_response_headers=linphone_chat_process_response_headers_from_get_file;
|
||||
cbs.process_response=linphone_chat_process_response_from_get_file;
|
||||
cbs.process_io_error=process_io_error;
|
||||
cbs.process_auth_requested=process_auth_requested;
|
||||
l=belle_http_request_listener_create_from_callbacks(&cbs, (void *)message);
|
||||
belle_sip_object_data_set(BELLE_SIP_OBJECT(req),"message",(void *)message,NULL);
|
||||
belle_http_provider_send_request(message->chat_room->lc->http_provider,req,l);
|
||||
}
|
||||
/**
|
||||
* Set origin of the message
|
||||
*@param message #LinphoneChatMessage obj
|
||||
|
|
@ -834,6 +1134,7 @@ static void _linphone_chat_message_destroy(LinphoneChatMessage* msg) {
|
|||
if (msg->from) linphone_address_destroy(msg->from);
|
||||
if (msg->to) linphone_address_destroy(msg->to);
|
||||
if (msg->custom_headers) sal_custom_header_free(msg->custom_headers);
|
||||
if (msg->content_type) ms_free(msg->content_type);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -868,6 +1169,27 @@ LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage* msg) {
|
|||
return linphone_error_info_get_reason(linphone_chat_message_get_error_info(msg));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a message attached to a dedicated chat room with a particular content. Use #linphone_chat_room_send_message2 to initiate the transfer
|
||||
* @param cr the chat room.
|
||||
* @param a #LinphoneContent initial content. #LinphoneCoreVTable.file_transfer_send is invoked later to notify file transfer progress and collect next chunk of the message if #LinphoneContent.data is NULL.
|
||||
* @return a new #LinphoneChatMessage
|
||||
*/
|
||||
|
||||
LinphoneChatMessage* linphone_chat_room_create_file_transfer_message(LinphoneChatRoom *cr, LinphoneContent* initial_content) {
|
||||
LinphoneChatMessage* msg = belle_sip_object_new(LinphoneChatMessage);
|
||||
msg->chat_room=(LinphoneChatRoom*)cr;
|
||||
msg->message = NULL;
|
||||
msg->file_transfer_information = initial_content;
|
||||
msg->dir=LinphoneChatMessageOutgoing;
|
||||
linphone_chat_message_set_to(msg, linphone_chat_room_get_peer_address(cr));
|
||||
linphone_chat_message_set_from(msg, linphone_address_new(linphone_core_get_identity(cr->lc)));
|
||||
msg->content_type=NULL; /* this will be set to application/vnd.gsma.rcs-ft-http+xml when we will transfer the xml reply from server to the peers */
|
||||
|
||||
return msg;
|
||||
}
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ notify_LDADD=$(helloworld_LDADD)
|
|||
filetransfer_SOURCES=filetransfer.c
|
||||
LINPHONE_TUTOS+=$(filetransfer_SOURCES)
|
||||
|
||||
filetransfer_LDADD=$(helloworld_LDADD)
|
||||
|
||||
AM_CFLAGS=\
|
||||
-I$(top_srcdir)/coreapi \
|
||||
$(STRICT_OPTIONS) \
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ static void file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMess
|
|||
const LinphoneAddress* from_address = linphone_chat_message_get_from(message);
|
||||
const LinphoneAddress* to_address = linphone_chat_message_get_to(message);
|
||||
char *address = linphone_chat_message_is_outgoing(message)?linphone_address_as_string(to_address):linphone_address_as_string(from_address);
|
||||
printf(" File transfer [%i&] %s of type [%s/%s] %s [%s] \n", (int)(content->size/progress)*100
|
||||
printf(" File transfer [%d%%] %s of type [%s/%s] %s [%s] \n", (int)progress
|
||||
,(linphone_chat_message_is_outgoing(message)?"sent":"received")
|
||||
, content->type
|
||||
, content->subtype
|
||||
|
|
@ -73,31 +73,32 @@ static void file_transfer_received(LinphoneCore *lc, LinphoneChatMessage *messag
|
|||
, from);
|
||||
if (!linphone_chat_message_get_user_data(message)) {
|
||||
/*first chunk, creating file*/
|
||||
file = open("receive_file.dump",O_WRONLY);
|
||||
file = open("receive_file.dump",O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
|
||||
linphone_chat_message_set_user_data(message,(void*)(long)(0x00000000FFFFFFFF&file)); /*store fd for next chunks*/
|
||||
} else {
|
||||
/*next chunk*/
|
||||
file = (int)linphone_chat_message_get_user_data(message);
|
||||
file = (int)((long)(linphone_chat_message_get_user_data(message))&0x00000000FFFFFFFF);
|
||||
}
|
||||
|
||||
/*store content on a file*/
|
||||
write(file,buff,size);
|
||||
|
||||
if (size==0) {
|
||||
printf("File transfert completed");
|
||||
printf("File transfert completed\n");
|
||||
close(file);
|
||||
} /*else wait for next chunk*/
|
||||
running=FALSE;
|
||||
} else { /* store content on a file*/
|
||||
write(file,buff,size);
|
||||
}
|
||||
|
||||
|
||||
free(from);
|
||||
}
|
||||
|
||||
char big_file [128000];
|
||||
/*
|
||||
* function call when is file transfer is initiated. file content should be feed into object LinphoneContent
|
||||
* function called when the file transfer is initiated. file content should be feed into object LinphoneContent
|
||||
* */
|
||||
static void file_transfer_send(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size){
|
||||
const LinphoneAddress* to_address = linphone_chat_message_get_to(message);
|
||||
char *to = linphone_address_as_string(to_address);
|
||||
//const LinphoneAddress* to_address = linphone_chat_message_get_to(message);
|
||||
//char *to = linphone_address_as_string(to_address);
|
||||
int offset=-1;
|
||||
/*content->size can be feed*/
|
||||
|
||||
|
|
@ -106,7 +107,7 @@ static void file_transfer_send(LinphoneCore *lc, LinphoneChatMessage *message,
|
|||
offset=0;
|
||||
} else {
|
||||
/*subsequent chunk*/
|
||||
offset = (int)linphone_chat_message_get_user_data(message);
|
||||
offset = (int)((long)(linphone_chat_message_get_user_data(message))&0x00000000FFFFFFFF);
|
||||
}
|
||||
*size = MIN(*size,sizeof(big_file)-offset); /*updating content->size with minimun between remaining data and requested size*/
|
||||
|
||||
|
|
@ -119,11 +120,22 @@ static void file_transfer_send(LinphoneCore *lc, LinphoneChatMessage *message,
|
|||
printf(" File transfer sending [%i] bytes of type [%s/%s] from [%s] \n" , (int)*size
|
||||
, content->type
|
||||
, content->subtype
|
||||
, to);
|
||||
, "pipo");
|
||||
/*store offset for next chunk*/
|
||||
linphone_chat_message_set_user_data(message,(void*)(offset+*size));
|
||||
|
||||
free(to);
|
||||
//free(to);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Call back called when a message is received
|
||||
*/
|
||||
static void message_received(LinphoneCore *lc, LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
|
||||
const LinphoneContent *file_transfer_info = linphone_chat_message_get_file_transfer_information(msg);
|
||||
printf ("Do you really want to download %s (size %ld)?[Y/n]\nOk, let's go\n", file_transfer_info->name, file_transfer_info->size);
|
||||
|
||||
linphone_chat_message_start_file_download(msg);
|
||||
|
||||
}
|
||||
/*
|
||||
|
|
@ -137,21 +149,21 @@ static void linphone_file_transfer_state_changed(LinphoneChatMessage* msg,Linpho
|
|||
free(to);
|
||||
}
|
||||
|
||||
|
||||
LinphoneCore *lc;
|
||||
int main(int argc, char *argv[]){
|
||||
LinphoneCoreVTable vtable={0};
|
||||
|
||||
char* dest_friend=NULL;
|
||||
const char* dest_friend=NULL;
|
||||
int i;
|
||||
const char* big_file_content="big file";
|
||||
/*seting dummy file content to something*/
|
||||
for (i=0;i<sizeof(big_file)/strlen(big_file_content);i++)
|
||||
sprintf(big_file+i,"%s",big_file_content);
|
||||
for (i=0;i<sizeof(big_file);i+=strlen(big_file_content))
|
||||
memcpy(big_file+i, big_file_content, strlen(big_file_content));
|
||||
|
||||
big_file[0]=*"S";
|
||||
big_file[sizeof(big_file)-1]=*"E";
|
||||
|
||||
/* takes sip uri identity from the command line arguments */
|
||||
if (argc>1){
|
||||
dest_friend=argv[1];
|
||||
}
|
||||
signal(SIGINT,stop);
|
||||
//#define DEBUG
|
||||
#ifdef DEBUG
|
||||
|
|
@ -166,20 +178,24 @@ int main(int argc, char *argv[]){
|
|||
vtable.file_transfer_received=file_transfer_received;
|
||||
vtable.file_transfer_send=file_transfer_send;
|
||||
vtable.file_transfer_progress_indication=file_transfer_progress_indication;
|
||||
vtable.message_received=message_received;
|
||||
|
||||
|
||||
/*
|
||||
Instantiate a LinphoneCore object given the LinphoneCoreVTable
|
||||
*/
|
||||
lc=linphone_core_new(&vtable,NULL,NULL,NULL);
|
||||
dest_friend = linphone_core_get_primary_contact(lc);
|
||||
printf("Send message to me : %s\n", dest_friend);
|
||||
|
||||
/**
|
||||
* Globally configure an http file transfer server.
|
||||
*/
|
||||
linphone_core_set_file_transfer_server(lc,"http://sharing.linphone.org/upload.php");
|
||||
linphone_core_set_file_transfer_server(lc,"http://npasc.al/lft.php");
|
||||
//linphone_core_set_file_transfer_server(lc,"https://www.linphone.org:444/upload.php");
|
||||
|
||||
|
||||
/*Next step is to create a chat root*/
|
||||
/*Next step is to create a chat room*/
|
||||
LinphoneChatRoom* chat_room = linphone_core_create_chat_room(lc,dest_friend);
|
||||
|
||||
LinphoneContent content;
|
||||
|
|
@ -187,13 +203,16 @@ int main(int argc, char *argv[]){
|
|||
content.type="text";
|
||||
content.subtype="plain";
|
||||
content.size=sizeof(big_file); /*total size to be transfered*/
|
||||
content.name = "bigfile.txt";
|
||||
|
||||
/*now create a chat message with custom content*/
|
||||
LinphoneChatMessage* chat_message = linphone_chat_room_create_file_transfer_message(chat_room,&content);
|
||||
if (chat_message == NULL) {
|
||||
printf("returned message is null\n");
|
||||
}
|
||||
|
||||
/*initiating file transfer*/
|
||||
/**/
|
||||
linphone_chat_room_send_message2(chat_room, chat_message,linphone_file_transfer_state_changed,NULL);
|
||||
linphone_chat_room_send_message2(chat_room, chat_message, linphone_file_transfer_state_changed, NULL);
|
||||
|
||||
/* main loop for receiving incoming messages and doing background linphone core work: */
|
||||
while(running){
|
||||
|
|
|
|||
|
|
@ -1392,6 +1392,8 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab
|
|||
lc->http_verify_policy = belle_tls_verify_policy_new();
|
||||
belle_http_provider_set_tls_verify_policy(lc->http_provider,lc->http_verify_policy);
|
||||
|
||||
lc->file_transfer_server = NULL;
|
||||
|
||||
certificates_config_read(lc);
|
||||
|
||||
remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
|
||||
|
|
@ -5891,6 +5893,8 @@ static void linphone_core_uninit(LinphoneCore *lc)
|
|||
lc->last_recv_msg_ids=ms_list_free(lc->last_recv_msg_ids);
|
||||
|
||||
// Free struct variable
|
||||
ms_free(lc->file_transfer_server);
|
||||
|
||||
if(lc->zrtp_secrets_cache != NULL) {
|
||||
ms_free(lc->zrtp_secrets_cache);
|
||||
}
|
||||
|
|
@ -6536,3 +6540,11 @@ bool_t linphone_core_sdp_200_ack_enabled(const LinphoneCore *lc) {
|
|||
return lc->sip_conf.sdp_200_ack!=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Globaly set an http file transfer server to be used for content type application/vnd.gsma.rcs-ft-http+xml. This value can also be set for a dedicated account using #linphone_proxy_config_set_file_transfer_server
|
||||
* @param #LinphoneCore to be modified
|
||||
* @param const char* url of the file server like https://file.linphone.org/upload.php
|
||||
**/
|
||||
void linphone_core_set_file_transfer_server(LinphoneCore *core, const char * server_url) {
|
||||
core->file_transfer_server=ms_strdup(server_url);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ struct _LinphoneContent{
|
|||
size_t size; /**<the size of the data buffer, excluding null character despite null character is always set for convenience.
|
||||
When provided by callback #LinphoneCoreFileTransferSendCb or #LinphoneCoreFileTransferReceiveCb, it states the total number of bytes of the transfered file*/
|
||||
char *encoding; /**<The encoding of the data buffer, for example "gzip"*/
|
||||
char *name; /**< used by RCS File transfer messages to store the original filename of the file to be downloaded from server */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1124,6 +1125,8 @@ LINPHONE_PUBLIC void linphone_chat_message_set_to(LinphoneChatMessage* message,
|
|||
LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_message_get_to(const LinphoneChatMessage* message);
|
||||
LINPHONE_PUBLIC const char* linphone_chat_message_get_external_body_url(const LinphoneChatMessage* message);
|
||||
LINPHONE_PUBLIC void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message,const char* url);
|
||||
LINPHONE_PUBLIC const LinphoneContent* linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage* message);
|
||||
LINPHONE_PUBLIC void linphone_chat_message_start_file_download(const LinphoneChatMessage*message);
|
||||
LINPHONE_PUBLIC const char * linphone_chat_message_get_text(const LinphoneChatMessage* message);
|
||||
LINPHONE_PUBLIC time_t linphone_chat_message_get_time(const LinphoneChatMessage* message);
|
||||
LINPHONE_PUBLIC void* linphone_chat_message_get_user_data(const LinphoneChatMessage* message);
|
||||
|
|
|
|||
|
|
@ -151,6 +151,8 @@ struct _LinphoneChatMessage {
|
|||
bool_t is_read;
|
||||
unsigned int storage_id;
|
||||
SalOp *op;
|
||||
LinphoneContent *file_transfer_information;
|
||||
char *content_type;
|
||||
};
|
||||
|
||||
BELLE_SIP_DECLARE_VPTR(LinphoneChatMessage);
|
||||
|
|
@ -700,6 +702,7 @@ struct _LinphoneCore
|
|||
belle_tls_verify_policy_t *http_verify_policy;
|
||||
MSList *tones;
|
||||
LinphoneReason chat_deny_code;
|
||||
char *file_transfer_server;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ typedef struct SalMessage{
|
|||
const char *text;
|
||||
const char *url;
|
||||
const char *message_id;
|
||||
const char *content_type;
|
||||
time_t time;
|
||||
}SalMessage;
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 77022250a04459648101c3b4152d83158bbe0e63
|
||||
Subproject commit fc3a434df7845f20e0fc3415f25bbad4598fe3be
|
||||
2
oRTP
2
oRTP
|
|
@ -1 +1 @@
|
|||
Subproject commit 519fabfed1b38c9cde977d4c7a8a7c2bb642e0cb
|
||||
Subproject commit c93363ac023c2122bfdfb8b0d99b811dffbad827
|
||||
Loading…
Add table
Reference in a new issue