sal_op_message.c 10.1 KB
Newer Older
jehan's avatar
jehan committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
linphone
Copyright (C) 2012  Belledonne Communications, Grenoble, France

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
jehan's avatar
jehan committed
18 19 20
*/
#include "sal_impl.h"

21
#include "linphone/core.h"
22 23 24
#include "private.h"
#include <libxml/xmlwriter.h>

25 26
static void process_error( SalOp* op) {
	if (op->dir == SalOpDirOutgoing) {
27
		op->base.root->callbacks.text_delivery_update(op, SalTextDeliveryFailed);
28 29 30 31 32 33 34 35 36
	} else {
		ms_warning("unexpected io error for incoming message on op [%p]",op);
	}
	op->state=SalOpStateTerminated;

}

static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
	SalOp* op = (SalOp*)user_ctx;
37
	sal_error_info_set(&op->error_info,SalReasonIOError,503,"IO Error",NULL);
38 39 40 41
	process_error(op);
}
static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
	SalOp* op=(SalOp*)user_ctx;
42
	sal_error_info_set(&op->error_info,SalReasonRequestTimeout,408,"Request timeout",NULL);
43
	process_error(op);
44

45 46 47 48
}
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));
49
	SalTextDeliveryStatus status;
50
	sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event));
51
	
52 53 54 55 56 57
	if (code>=100 && code <200)
		status=SalTextDeliveryInProgress;
	else if (code>=200 && code <300)
		status=SalTextDeliveryDone;
	else
		status=SalTextDeliveryFailed;
58 59
	
	op->base.root->callbacks.text_delivery_update(op,status);
60
}
61

62 63 64
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;
jehan's avatar
jehan committed
65
}
66 67 68 69
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;
}
jehan's avatar
jehan committed
70

Ghislain MARY's avatar
Ghislain MARY committed
71 72 73 74 75
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);
}

76
static void add_message_accept(belle_sip_message_t *msg){
Ghislain MARY's avatar
Ghislain MARY committed
77
	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"));
78 79 80
}

void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *event){
81 82 83 84 85 86
	belle_sip_request_t* req = belle_sip_request_event_get_request(event);
	belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,req);
	belle_sip_header_address_t* address;
	belle_sip_header_from_t* from_header;
	belle_sip_header_content_type_t* content_type;
	belle_sip_response_t* resp;
87
	int errcode = 500;
88 89
	belle_sip_header_call_id_t* call_id = belle_sip_message_get_header_by_type(req,belle_sip_header_call_id_t);
	belle_sip_header_cseq_t* cseq = belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t);
90
	belle_sip_header_date_t *date=belle_sip_message_get_header_by_type(req,belle_sip_header_date_t);
91 92 93 94 95
	char* from;
	bool_t external_body=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);
Simon Morlat's avatar
Simon Morlat committed
96
	
97
	if (content_type) {
98 99 100 101 102
		
		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);
			
103 104 105 106 107 108 109 110 111 112
		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);
Ghislain MARY's avatar
Ghislain MARY committed
113 114 115 116 117 118 119 120 121 122
		} 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);
123
		} else {
Simon Morlat's avatar
Simon Morlat committed
124 125
			SalMessage salmsg;
			char message_id[256]={0};
126 127
			
			external_body=is_external_body(content_type);
Simon Morlat's avatar
Simon Morlat committed
128 129 130 131 132 133 134 135 136
		
			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)*/
137
			salmsg.text=(!external_body)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
Simon Morlat's avatar
Simon Morlat committed
138
			salmsg.url=NULL;
139
			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));
Simon Morlat's avatar
Simon Morlat committed
140 141 142 143 144 145 146 147 148 149 150 151
			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);
152
		}
153
	} else {
Simon Morlat's avatar
Simon Morlat committed
154 155
		ms_error("Unsupported MESSAGE (no Content-Type)");
		goto error;
156
	}
Simon Morlat's avatar
Simon Morlat committed
157 158 159
	return;
error:
	resp = belle_sip_response_create_from_request(req, errcode);
Simon Morlat's avatar
Simon Morlat committed
160
	add_message_accept((belle_sip_message_t*)resp);
Simon Morlat's avatar
Simon Morlat committed
161
	belle_sip_server_transaction_send_response(server_transaction,resp);
Simon Morlat's avatar
Simon Morlat committed
162
	sal_op_release(op);
163 164 165 166 167
}

static void process_request_event(void *op_base, const belle_sip_request_event_t *event) {
	SalOp* op = (SalOp*)op_base;
	sal_process_incoming_message(op,event);
168
}
169

170
int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg, const char *peer_uri){
jehan's avatar
jehan committed
171
	belle_sip_request_t* req;
172
	char content_type_raw[256];
173
	size_t content_length = msg?strlen(msg):0;
174
	time_t curtime = ms_time(NULL);
175
	const char *body;
johan's avatar
johan committed
176
	int retval;
177
	
178 179 180 181 182 183 184 185 186 187 188 189
	if (op->dialog){
		/*for SIP MESSAGE that are sent in call's dialog*/
		req=belle_sip_dialog_create_queued_request(op->dialog,"MESSAGE");
	}else{
		sal_op_message_fill_cbs(op);
		if (from)
			sal_op_set_from(op,from);
		if (to)
			sal_op_set_to(op,to);
		op->dir=SalOpDirOutgoing;

		req=sal_op_build_request(op,"MESSAGE");
190 191 192
		if (req == NULL ){
			return -1;
		}
193 194 195
		if (sal_op_get_contact_address(op)){
			belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op)));
		}
196
	}
197

198 199
	snprintf(content_type_raw,sizeof(content_type_raw),BELLE_SIP_CONTENT_TYPE ": %s",content_type);
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_type_parse(content_type_raw)));
jehan's avatar
jehan committed
200
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length)));
201
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_date_create_from_time(&curtime)));
202
	body = msg;
203 204 205 206
	if (body){
		/*don't call set_body() with null argument because it resets content type and content length*/
		belle_sip_message_set_body(BELLE_SIP_MESSAGE(req), body, content_length);
	}
johan's avatar
johan committed
207
	retval = sal_op_send_request(op,req);
208

johan's avatar
johan committed
209
	return retval;
210
}
211

Simon Morlat's avatar
Simon Morlat committed
212 213 214 215 216 217 218 219 220 221 222
int sal_message_reply(SalOp *op, SalReason reason){
	if (op->pending_server_trans){
		int code=sal_reason_to_sip_code(reason);
		belle_sip_response_t *resp = belle_sip_response_create_from_request(
			belle_sip_transaction_get_request((belle_sip_transaction_t*)op->pending_server_trans),code);
		belle_sip_server_transaction_send_response(op->pending_server_trans,resp);
		return 0;
	}else ms_error("sal_message_reply(): no server transaction");
	return -1;
}

223
int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg) {
224
	return sal_message_send(op,from,to,"text/plain",msg, NULL);
jehan's avatar
jehan committed
225
}
226

227 228
static belle_sip_listener_callbacks_t op_message_callbacks={0};

229
void sal_op_message_fill_cbs(SalOp*op) {
230 231 232 233 234 235 236
	if (op_message_callbacks.process_io_error==NULL){
		op_message_callbacks.process_io_error=process_io_error;
		op_message_callbacks.process_response_event=process_response_event;
		op_message_callbacks.process_timeout=process_timeout;
		op_message_callbacks.process_request_event=process_request_event;
	}
	op->callbacks=&op_message_callbacks;
237
	op->type=SalOpMessage;
238
}