callbacks.c 28.4 KB
Newer Older
Simon Morlat's avatar
Simon Morlat committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
linphone
Copyright (C) 2010  Simon MORLAT (simon.morlat@free.fr)

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.
Simon Morlat's avatar
Simon Morlat committed
18 19 20
*/


21 22 23
#include "c-wrapper/internal/c-sal.h"
#include "sal/call-op.h"
#include "sal/message-op.h"
24
#include "sal/refer-op.h"
Simon Morlat's avatar
Simon Morlat committed
25

26
#include "linphone/core.h"
27
#include "linphone/utils/utils.h"
Simon Morlat's avatar
Simon Morlat committed
28
#include "private.h"
29
#include "mediastreamer2/mediastream.h"
30
#include "linphone/lpconfig.h"
31
#include <bctoolbox/defs.h>
32

33
// stat
34
#ifndef _WIN32
35 36 37 38 39
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

40
#include "c-wrapper/c-wrapper.h"
41
#include "call/call-p.h"
Ronan's avatar
Ronan committed
42
#include "chat/chat-room/chat-room.h"
43
#include "conference/participant.h"
44
#include "conference/session/call-session-p.h"
45
#include "conference/session/call-session.h"
46
#include "conference/session/media-session-p.h"
47
#include "conference/session/media-session.h"
48
#include "core/core-p.h"
49

50
using namespace LinphonePrivate;
51

52
static void register_failure(SalOp *op);
53

54
static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const LinphoneAddress *remote) {
55
	bctbx_list_t *elem;
jehan's avatar
jehan committed
56
	ms_message("Searching for already_a_call_with_remote_address.");
57 58 59 60 61 62 63 64 65 66 67 68

	for(elem=lc->calls;elem!=NULL;elem=elem->next){
		const LinphoneCall *call=(LinphoneCall*)elem->data;
		const LinphoneAddress *cRemote=linphone_call_get_remote_address(call);
		if (linphone_address_weak_equal(cRemote,remote)) {
			ms_warning("already_a_call_with_remote_address found.");
			return TRUE;
		}
	}
	return FALSE;
}

Simon Morlat's avatar
Simon Morlat committed
69

70
static LinphoneCall * look_for_broken_call_to_replace(LinphonePrivate::SalOp *h, LinphoneCore *lc) {
71 72 73
	const bctbx_list_t *calls = linphone_core_get_calls(lc);
	const bctbx_list_t *it = calls;
	while (it != NULL) {
74
#if 0
Ronan's avatar
Ronan committed
75
		LinphoneCall *replaced_call = NULL;
76
		LinphoneCall *call = (LinphoneCall *)bctbx_list_get_data(it);
77 78
		SalOp *replaced_op = sal_call_get_replaces(h);
		if (replaced_op) replaced_call = (LinphoneCall*)sal_op_get_user_pointer(replaced_op);
Ghislain MARY's avatar
Ghislain MARY committed
79 80
		if ((call->broken && sal_call_compare_op(h, call->op))
			|| ((replaced_call == call) && (strcmp(sal_op_get_from(h), sal_op_get_from(replaced_op)) == 0) && (strcmp(sal_op_get_to(h), sal_op_get_to(replaced_op)) == 0))) {
81 82
			return call;
		}
83
#endif
84 85
		it = bctbx_list_next(it);
	}
86

87 88 89
	return NULL;
}

90
static void call_received(SalCallOp *h) {
91
	/* Look if this INVITE is for a call that has already been notified but broken because of network failure */
92
	LinphoneCore *lc = reinterpret_cast<LinphoneCore *>(h->get_sal()->get_user_pointer());
93 94 95
	LinphoneCall *replacedCall = look_for_broken_call_to_replace(h, lc);
	if (replacedCall) {
		linphone_call_replace_op(replacedCall, h);
96 97
		return;
	}
98

99
	LinphoneAddress *fromAddr = nullptr;
100
	const char *pAssertedId = sal_custom_header_find(h->get_recv_custom_header(), "P-Asserted-Identity");
101 102 103 104 105
	/* In some situation, better to trust the network rather than the UAC */
	if (lp_config_get_int(linphone_core_get_config(lc), "sip", "call_logs_use_asserted_id_instead_of_from", 0)) {
		if (pAssertedId) {
			LinphoneAddress *pAssertedIdAddr = linphone_address_new(pAssertedId);
			if (pAssertedIdAddr) {
106
				ms_message("Using P-Asserted-Identity [%s] instead of from [%s] for op [%p]", pAssertedId, h->get_from(), h);
107 108 109 110 111 112 113 114
				fromAddr = pAssertedIdAddr;
			} else
				ms_warning("Unsupported P-Asserted-Identity header for op [%p] ", h);
		} else
			ms_warning("No P-Asserted-Identity header found so cannot use it for op [%p] instead of from", h);
	}

	if (!fromAddr)
115 116
		fromAddr = linphone_address_new(h->get_from());
	LinphoneAddress *toAddr = linphone_address_new(h->get_to());
117 118 119 120

	/* First check if we can answer successfully to this invite */
	LinphonePresenceActivity *activity = nullptr;
	if ((linphone_presence_model_get_basic_status(lc->presence_model) == LinphonePresenceBasicStatusClosed)
121
		&& (activity = linphone_presence_model_get_activity(lc->presence_model))) {
122
		char *altContact = nullptr;
123 124
		switch (linphone_presence_activity_get_type(activity)) {
			case LinphonePresenceActivityPermanentAbsence:
125 126
				altContact = linphone_presence_model_get_contact(lc->presence_model);
				if (altContact) {
Ghislain MARY's avatar
Ghislain MARY committed
127 128
					SalErrorInfo sei;
					memset(&sei, 0, sizeof(sei));
129
					sal_error_info_set(&sei, SalReasonRedirect, "SIP", 0, nullptr, nullptr);
130 131
					SalAddress *altAddr = sal_address_new(altContact);
					h->decline_with_error_info(&sei, altAddr);
132
					ms_free(altContact);
133
					sal_address_unref(altAddr);
134 135 136
					LinphoneErrorInfo *ei = linphone_error_info_new();
					linphone_error_info_set(ei, nullptr, LinphoneReasonMovedPermanently, 302, "Moved permanently", nullptr);
					linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, fromAddr, toAddr, ei);
137
					h->release();
138
					sal_error_info_reset(&sei);
139
					return;
140
				}
141 142
				break;
			default:
143
				/* Nothing special to be done */
144
				break;
145
		}
Simon Morlat's avatar
Simon Morlat committed
146
	}
147

148
	if (!linphone_core_can_we_add_call(lc)) { /* Busy */
149
		h->decline(SalReasonBusy, nullptr);
150 151 152
		LinphoneErrorInfo *ei = linphone_error_info_new();
		linphone_error_info_set(ei, nullptr, LinphoneReasonBusy, 486, "Busy - too many calls", nullptr);
		linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, fromAddr, toAddr, ei);
153
		h->release();
Simon Morlat's avatar
Simon Morlat committed
154 155
		return;
	}
156

157 158
	/* Check if I'm the caller */
	LinphoneAddress *fromAddressToSearchIfMe = nullptr;
159
	if (h->get_privacy() == SalPrivacyNone)
160 161 162 163 164 165 166 167
		fromAddressToSearchIfMe = linphone_address_clone(fromAddr);
	else if (pAssertedId)
		fromAddressToSearchIfMe = linphone_address_new(pAssertedId);
	else
		ms_warning("Hidden from identity, don't know if it's me");
	if (fromAddressToSearchIfMe && already_a_call_with_remote_address(lc, fromAddressToSearchIfMe)) {
		char *addr = linphone_address_as_string(fromAddr);
		ms_warning("Receiving a call while one with same address [%s] is initiated, refusing this one with busy message", addr);
168
		h->decline(SalReasonBusy, nullptr);
169 170 171
		LinphoneErrorInfo *ei = linphone_error_info_new();
		linphone_error_info_set(ei, nullptr, LinphoneReasonBusy, 486, "Busy - duplicated call", nullptr);
		linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, fromAddr, toAddr, ei);
172
		h->release();
173
		linphone_address_unref(fromAddressToSearchIfMe);
174
		ms_free(addr);
Simon Morlat's avatar
Simon Morlat committed
175 176
		return;
	}
177 178
	if (fromAddressToSearchIfMe)
		linphone_address_unref(fromAddressToSearchIfMe);
179

180 181 182
	LinphoneCall *call = linphone_call_new_incoming(lc, fromAddr, toAddr, h);
	linphone_address_unref(fromAddr);
	linphone_address_unref(toAddr);
183
	L_GET_PRIVATE_FROM_C_OBJECT(call)->startIncomingNotification();
184 185
}

186 187
static void call_rejected(SalCallOp *h){
	LinphoneCore *lc=(LinphoneCore *)h->get_sal()->get_user_pointer();
188 189
	LinphoneErrorInfo *ei = linphone_error_info_new();
	linphone_error_info_from_sal_op(ei, h);
190
	linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, linphone_address_new(h->get_from()), linphone_address_new(h->get_to()), ei);
191 192
}

193
#if 0
194 195 196 197 198 199 200 201
static void start_remote_ring(LinphoneCore *lc, LinphoneCall *call) {
	if (lc->sound_conf.play_sndcard!=NULL){
		MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
		if (call->localdesc->streams[0].max_rate>0) ms_snd_card_set_preferred_sample_rate(ringcard, call->localdesc->streams[0].max_rate);
		/*we release sound before playing ringback tone*/
		if (call->audiostream)
			audio_stream_unprepare_sound(call->audiostream);
		if( lc->sound_conf.remote_ring ){
202
			lc->ringstream=ring_start(lc->factory, lc->sound_conf.remote_ring,2000,ringcard);
203 204 205
		}
	}
}
Simon Morlat's avatar
Simon Morlat committed
206
#endif
207

208
static void call_ringing(SalOp *h) {
209
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(h->get_user_pointer());
210 211
	if (!session) return;
	L_GET_PRIVATE(session)->remoteRinging();
212 213
}

214
#if 0
Ronan's avatar
Ronan committed
215
static void start_pending_refer(LinphoneCall *call){
216
	linphone_core_start_refered_call(call->core, call,NULL);
217
}
Ronan's avatar
Ronan committed
218
#endif
219

220 221 222 223 224
/*
 * could be reach :
 *  - when the call is accepted
 *  - when a request is accepted (pause, resume)
 */
225
static void call_accepted(SalOp *op) {
226
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
227 228 229
	if (!session) {
		ms_warning("call_accepted: CallSession no longer exists");
		return;
Simon Morlat's avatar
Simon Morlat committed
230
	}
231
	L_GET_PRIVATE(session)->accepted();
232 233
}

234
/* this callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session*/
235
static void call_updating(SalOp *op, bool_t is_update) {
236
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
237 238 239
	if (!session) {
		ms_warning("call_updating: CallSession no longer exists");
		return;
240
	}
241
	L_GET_PRIVATE(session)->updating(!!is_update);
242 243 244
}


245
static void call_ack_received(SalOp *op, SalCustomHeader *ack) {
246
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
247 248
	if (!session) {
		ms_warning("call_ack_received(): no CallSession for which an ack is expected");
249 250
		return;
	}
251
	L_GET_PRIVATE(session)->ackReceived(reinterpret_cast<LinphoneHeaders *>(ack));
252 253
}

254

255
static void call_ack_being_sent(SalOp *op, SalCustomHeader *ack) {
256
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
257 258
	if (!session) {
		ms_warning("call_ack_being_sent(): no CallSession for which an ack is supposed to be sent");
259 260
		return;
	}
261
	L_GET_PRIVATE(session)->ackBeingSent(reinterpret_cast<LinphoneHeaders *>(ack));
262 263
}

264
static void call_terminated(SalOp *op, const char *from) {
265
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
266 267 268
	if (!session)
		return;
	L_GET_PRIVATE(session)->terminated();
269 270
}

271
#if 0
Ronan's avatar
Ronan committed
272
static int resume_call_after_failed_transfer(LinphoneCall *call){
273 274
	if (call->was_automatically_paused && call->state==LinphoneCallPausing)
		return BELLE_SIP_CONTINUE; /*was still in pausing state*/
275

276 277
	if (call->was_automatically_paused && call->state==LinphoneCallPaused){
		if (sal_op_is_idle(call->op)){
278
			linphone_call_resume(call);
279
		}else {
280
			ms_message("resume_call_after_failed_transfer(), salop was busy");
281 282 283 284 285 286
			return BELLE_SIP_CONTINUE;
		}
	}
	linphone_call_unref(call);
	return BELLE_SIP_STOP;
}
Ronan's avatar
Ronan committed
287
#endif
288

289
static void call_failure(SalOp *op) {
290
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
291 292 293
	if (!session) {
		ms_warning("Failure reported on already terminated CallSession");
		return;
294
	}
295
	L_GET_PRIVATE(session)->failure();
296 297
}

298
static void call_released(SalOp *op) {
299
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
300 301 302 303
	if (!session) {
		/* We can get here when the core manages call at Sal level without creating a Call object. Typicially,
		 * when declining an incoming call with busy because maximum number of calls is reached. */
		return;
Simon Morlat's avatar
Simon Morlat committed
304
	}
305
	L_GET_PRIVATE(session)->setState(LinphoneCallReleased, "Call released");
306 307
}

308
static void call_cancel_done(SalOp *op) {
309
#if 0
310 311 312 313 314
	LinphoneCall *call = (LinphoneCall *)sal_op_get_user_pointer(op);
	if (call->reinvite_on_cancel_response_requested == TRUE) {
		call->reinvite_on_cancel_response_requested = FALSE;
		linphone_call_reinvite_to_recover_from_connection_loss(call);
	}
315
#endif
316 317
}

jehan's avatar
jehan committed
318
static void auth_failure(SalOp *op, SalAuthInfo* info) {
319
	LinphoneCore *lc = reinterpret_cast<LinphoneCore *>(op->get_sal()->get_user_pointer());
320
	LinphoneAuthInfo *ai = NULL;
321

322 323
	if (info != NULL) {
		ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, info->realm, info->username, info->domain, TRUE);
324
		if (ai){
325
			LinphoneAuthMethod method = info->mode == SalAuthModeHttpDigest ? LinphoneAuthHttpDigest : LinphoneAuthTls;
326
			LinphoneAuthInfo *auth_info = linphone_core_create_auth_info(lc, info->username, NULL, NULL, NULL, info->realm, info->domain);
327
			ms_message("%s/%s/%s/%s authentication fails.", info->realm, info->username, info->domain, info->mode == SalAuthModeHttpDigest ? "HttpDigest" : "Tls");
328
			/*ask again for password if auth info was already supplied but apparently not working*/
329
			linphone_core_notify_authentication_requested(lc, auth_info, method);
Ghislain MARY's avatar
Ghislain MARY committed
330
			linphone_auth_info_unref(auth_info);
331 332
			// Deprecated
			linphone_core_notify_auth_info_requested(lc, info->realm, info->username, info->domain);
333
		}
jehan's avatar
jehan committed
334
	}
335
}
336

337
static void register_success(SalOp *op, bool_t registered){
338
	LinphoneProxyConfig *cfg=(LinphoneProxyConfig *)op->get_user_pointer();
339 340
	if (!cfg){
		ms_message("Registration success for deleted proxy config, ignored");
341 342
		return;
	}
343
	linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared ,
344
									registered ? "Registration successful" : "Unregistration done");
345 346
}

347
static void register_failure(SalOp *op){
348 349
	LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)op->get_user_pointer();
	const SalErrorInfo *ei=op->get_error_info();
350
	const char *details=ei->full_string;
351 352 353 354 355 356

	if (cfg==NULL){
		ms_warning("Registration failed for unknown proxy config.");
		return ;
	}
	if (details==NULL)
357
		details="no response timeout";
358

359
	if ((ei->reason == SalReasonServiceUnavailable || ei->reason == SalReasonIOError)
360
			&& linphone_proxy_config_get_state(cfg) == LinphoneRegistrationOk) {
361
		linphone_proxy_config_set_state(cfg,LinphoneRegistrationProgress,"Service unavailable, retrying");
362 363 364
	} else {
		linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details);
	}
365
	if (cfg->presence_publish_event){
366
		/*prevent publish to be sent now until registration gets successful*/
367 368
		linphone_event_terminate(cfg->presence_publish_event);
		cfg->presence_publish_event=NULL;
369 370
		cfg->send_publish=cfg->publish;
	}
371 372
}

373
static void vfu_request(SalOp *op) {
374
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
375 376 377 378 379 380
	if (!session)
		return;
	LinphonePrivate::MediaSession *mediaSession = dynamic_cast<LinphonePrivate::MediaSession *>(session);
	if (!mediaSession) {
		ms_warning("VFU request but no MediaSession!");
		return;
381
	}
382
	L_GET_PRIVATE(mediaSession)->sendVfu();
383 384 385
}

static void dtmf_received(SalOp *op, char dtmf){
386
#if 0
387
	LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
388
	if (!call) return;
389
	linphone_call_notify_dtmf_received(call, dtmf);
390
#endif
391 392
}

393
static void call_refer_received(SalOp *op, const SalAddress *referto){
394
#if 0
395
	LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
396
	LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
397 398
	LinphoneAddress *refer_to_addr = linphone_address_new(referto);
	char method[20] = "";
399

400 401 402
	if(refer_to_addr) {
		const char *tmp = linphone_address_get_method_param(refer_to_addr);
		if(tmp) strncpy(method, tmp, sizeof(method));
Simon Morlat's avatar
Simon Morlat committed
403
		linphone_address_unref(refer_to_addr);
404 405
	}
	if (call && (strlen(method) == 0 || strcmp(method, "INVITE") == 0)) {
406 407 408 409 410
		if (call->refer_to!=NULL){
			ms_free(call->refer_to);
		}
		call->refer_to=ms_strdup(referto);
		call->refer_pending=TRUE;
Simon Morlat's avatar
Simon Morlat committed
411
		linphone_call_set_state(call,LinphoneCallRefered,"Refered");
412
		if (call->refer_pending) linphone_core_start_refered_call(lc,call,NULL);
413 414
	}else {
		linphone_core_notify_refer_received(lc,referto);
415
	}
416
#endif
417 418
}

Ghislain MARY's avatar
Ghislain MARY committed
419
static void message_received(SalOp *op, const SalMessage *msg){
420 421
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
	LinphoneCall *call=(LinphoneCall*)op->get_user_pointer();
422
	LinphoneReason reason = lc->chat_deny_code;
423 424
	if (reason == LinphoneReasonNone) {
		linphone_core_message_received(lc, op, msg);
425
	}
Sylvain Berfini's avatar
Sylvain Berfini committed
426
	auto messageOp = dynamic_cast<SalMessageOpInterface *>(op);
427 428
	messageOp->reply(linphone_reason_to_sal(reason));
	if (!call) op->release();
429 430
}

431
static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) {
432
	linphone_notify_parse_presence(content_type, content_subtype, body, result);
433 434
}

435
static void convert_presence_to_xml_requested(SalOp *op, SalPresenceModel *presence, const char *contact, char **content) {
436
	/*for backward compatibility because still used by notify. No loguer used for publish*/
437

438 439 440
	if(linphone_presence_model_get_presentity((LinphonePresenceModel*)presence) == NULL) {
		LinphoneAddress * presentity = linphone_address_new(contact);
		linphone_presence_model_set_presentity((LinphonePresenceModel*)presence, presentity);
jehan's avatar
jehan committed
441
		linphone_address_unref(presentity);
442 443
	}
	*content = linphone_presence_model_to_xml((LinphonePresenceModel*)presence);
444 445
}

446
static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model, const char *msg){
447
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
448
	linphone_notify_recv(lc,op,ss,model);
449 450
}

451 452
static void subscribe_presence_received(SalPresenceOp *op, const char *from){
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
453
	linphone_subscription_new(lc,op,from);
454 455
}

456 457
static void subscribe_presence_closed(SalPresenceOp *op, const char *from){
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
458
	linphone_subscription_closed(lc,op);
459 460
}

461
static void ping_reply(SalOp *op) {
462
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
463 464 465
	if (!session) {
		ms_warning("Ping reply without CallSession attached...");
		return;
Simon Morlat's avatar
Simon Morlat committed
466
	}
467
	L_GET_PRIVATE(session)->pingReply();
468
}
jehan's avatar
jehan committed
469

470
static bool_t fill_auth_info_with_client_certificate(LinphoneCore *lc, SalAuthInfo* sai) {
471 472
	const char *chain_file = linphone_core_get_tls_cert_path(lc);
	const char *key_file = linphone_core_get_tls_key_path(lc);
473

474
	if (key_file && chain_file) {
475
#ifndef _WIN32
476 477 478 479 480 481 482 483 484 485
		// optinal check for files
		struct stat st;
		if (stat(key_file, &st)) {
			ms_warning("No client certificate key found in %s", key_file);
			return FALSE;
		}
		if (stat(chain_file, &st)) {
			ms_warning("No client certificate chain found in %s", chain_file);
			return FALSE;
		}
486
#endif
487 488 489 490 491 492
		sal_certificates_chain_parse_file(sai, chain_file, SAL_CERTIFICATE_RAW_FORMAT_PEM);
		sal_signing_key_parse_file(sai, key_file, "");
	} else if (lc->tls_cert && lc->tls_key) {
		sal_certificates_chain_parse(sai, lc->tls_cert, SAL_CERTIFICATE_RAW_FORMAT_PEM);
		sal_signing_key_parse(sai, lc->tls_key, "");
	}
Simon Morlat's avatar
Simon Morlat committed
493
	return sai->certificates && sai->key;
494 495
}

jehan's avatar
jehan committed
496
static bool_t fill_auth_info(LinphoneCore *lc, SalAuthInfo* sai) {
497 498 499 500 501 502
	LinphoneAuthInfo *ai = NULL;
	if (sai->mode == SalAuthModeTls) {
		ai = (LinphoneAuthInfo*)_linphone_core_find_tls_auth_info(lc);
	} else {
		ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc,sai->realm,sai->username,sai->domain, FALSE);
	}
jehan's avatar
jehan committed
503
	if (ai) {
504
		if (sai->mode == SalAuthModeHttpDigest) {
505 506 507
			sai->userid = ms_strdup(ai->userid ? ai->userid : ai->username);
			sai->password = ai->passwd?ms_strdup(ai->passwd) : NULL;
			sai->ha1 = ai->ha1 ? ms_strdup(ai->ha1) : NULL;
508 509 510 511 512 513 514 515 516 517 518
		} else if (sai->mode == SalAuthModeTls) {
			if (ai->tls_cert && ai->tls_key) {
				sal_certificates_chain_parse(sai, ai->tls_cert, SAL_CERTIFICATE_RAW_FORMAT_PEM);
				sal_signing_key_parse(sai, ai->tls_key, "");
			} else if (ai->tls_cert_path && ai->tls_key_path) {
				sal_certificates_chain_parse_file(sai, ai->tls_cert_path, SAL_CERTIFICATE_RAW_FORMAT_PEM);
				sal_signing_key_parse_file(sai, ai->tls_key_path, "");
			} else {
				fill_auth_info_with_client_certificate(lc, sai);
			}
		}
519

520 521 522 523 524
		if (sai->realm && !ai->realm){
			/*if realm was not known, then set it so that ha1 may eventually be calculated and clear text password dropped*/
			linphone_auth_info_set_realm(ai, sai->realm);
			linphone_core_write_auth_info(lc, ai);
		}
jehan's avatar
jehan committed
525 526
		return TRUE;
	} else {
527 528 529
		if (sai->mode == SalAuthModeTls) {
			return fill_auth_info_with_client_certificate(lc, sai);
		}
jehan's avatar
jehan committed
530 531 532
		return FALSE;
	}
}
jehan's avatar
jehan committed
533
static bool_t auth_requested(Sal* sal, SalAuthInfo* sai) {
534
	LinphoneCore *lc = (LinphoneCore *)sal->get_user_pointer();
535 536 537 538
	if (fill_auth_info(lc,sai)) {
		return TRUE;
	} else {
		LinphoneAuthMethod method = sai->mode == SalAuthModeHttpDigest ? LinphoneAuthHttpDigest : LinphoneAuthTls;
539 540
		LinphoneAuthInfo *ai = linphone_core_create_auth_info(lc, sai->username, NULL, NULL, NULL, sai->realm, sai->domain);
		linphone_core_notify_authentication_requested(lc, ai, method);
Ghislain MARY's avatar
Ghislain MARY committed
541
		linphone_auth_info_unref(ai);
542 543
		// Deprecated
		linphone_core_notify_auth_info_requested(lc, sai->realm, sai->username, sai->domain);
544
		if (fill_auth_info(lc, sai)) {
545
			return TRUE;
jehan's avatar
jehan committed
546 547 548 549
		}
		return FALSE;
	}
}
550

551
static void notify_refer(SalOp *op, SalReferStatus status){
552
	LinphoneCall *call=(LinphoneCall*) op->get_user_pointer();
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
	LinphoneCallState cstate;
	if (call==NULL) {
		ms_warning("Receiving notify_refer for unknown call.");
		return ;
	}
	switch(status){
		case SalReferTrying:
			cstate=LinphoneCallOutgoingProgress;
		break;
		case SalReferSuccess:
			cstate=LinphoneCallConnected;
		break;
		case SalReferFailed:
			cstate=LinphoneCallError;
		break;
		default:
			cstate=LinphoneCallError;
	}
571
	linphone_call_set_transfer_state(call, cstate);
572 573
	if (cstate==LinphoneCallConnected){
		/*automatically terminate the call as the transfer is complete.*/
574
		linphone_call_terminate(call);
575 576 577
	}
}

Ghislain MARY's avatar
Ghislain MARY committed
578
static LinphoneChatMessageState chatStatusSal2Linphone(SalMessageDeliveryStatus status){
579
	switch(status){
Ghislain MARY's avatar
Ghislain MARY committed
580
		case SalMessageDeliveryInProgress:
581
			return LinphoneChatMessageStateInProgress;
Ghislain MARY's avatar
Ghislain MARY committed
582
		case SalMessageDeliveryDone:
583
			return LinphoneChatMessageStateDelivered;
Ghislain MARY's avatar
Ghislain MARY committed
584
		case SalMessageDeliveryFailed:
585 586 587 588 589
			return LinphoneChatMessageStateNotDelivered;
	}
	return LinphoneChatMessageStateIdle;
}

Ghislain MARY's avatar
Ghislain MARY committed
590
static void message_delivery_update(SalOp *op, SalMessageDeliveryStatus status){
591
	LinphoneChatMessage *chat_msg=(LinphoneChatMessage* )op->get_user_pointer();
592 593 594 595 596

	if (chat_msg == NULL) {
		// Do not handle delivery status for isComposing messages.
		return;
	}
597
	// check that the message does not belong to an already destroyed chat room - if so, do not invoke callbacks
598
	if (linphone_chat_message_get_chat_room(chat_msg) != NULL) {
599
		linphone_chat_message_update_state(chat_msg, chatStatusSal2Linphone(status));
600
	}
Ghislain MARY's avatar
Ghislain MARY committed
601
	if (status != SalMessageDeliveryInProgress) { /*only release op if not in progress*/
602
		linphone_chat_message_unref(chat_msg);
jehan's avatar
jehan committed
603
	}
604 605
}

606
static void info_received(SalOp *op, SalBodyHandler *body_handler){
607
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
608
	linphone_core_notify_info_message(lc,op,body_handler);
Simon Morlat's avatar
Simon Morlat committed
609 610
}

611
static void subscribe_response(SalOp *op, SalSubscribeStatus status, int will_retry){
612
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
613

614
	if (lev==NULL) return;
615

616 617 618 619 620
	if (status==SalSubscribeActive){
		linphone_event_set_state(lev,LinphoneSubscriptionActive);
	}else if (status==SalSubscribePending){
		linphone_event_set_state(lev,LinphoneSubscriptionPending);
	}else{
621
		if (will_retry){
Simon Morlat's avatar
Simon Morlat committed
622 623 624
			linphone_event_set_state(lev,LinphoneSubscriptionOutgoingProgress);
		}
		else linphone_event_set_state(lev,LinphoneSubscriptionError);
625 626 627
	}
}

628 629 630
static void notify(SalSubscribeOp *op, SalSubscribeStatus st, const char *eventname, SalBodyHandler *body_handler){
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
631 632
	bool_t out_of_dialog = (lev==NULL);
	if (out_of_dialog) {
633 634
		/*out of dialog notify */
		lev = linphone_event_new_with_out_of_dialog_op(lc,op,LinphoneSubscriptionOutgoing,eventname);
635
	}
636
	{
637
		LinphoneContent *ct=linphone_content_from_sal_body_handler(body_handler);
Ghislain MARY's avatar
Ghislain MARY committed
638 639 640 641
		if (ct) {
			linphone_core_notify_notify_received(lc,lev,eventname,ct);
			linphone_content_unref(ct);
		}
642
	}
643 644 645
	if (out_of_dialog){
		/*out of dialog NOTIFY do not create an implicit subscription*/
		linphone_event_set_state(lev, LinphoneSubscriptionTerminated);
646
	}else if (st!=SalSubscribeNone){
647 648 649 650
		linphone_event_set_state(lev,linphone_subscription_state_from_sal(st));
	}
}

651 652 653
static void subscribe_received(SalSubscribeOp *op, const char *eventname, const SalBodyHandler *body_handler){
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
654

655
	if (lev==NULL) {
Simon Morlat's avatar
Simon Morlat committed
656
		lev=linphone_event_new_with_op(lc,op,LinphoneSubscriptionIncoming,eventname);
657 658 659 660
		linphone_event_set_state(lev,LinphoneSubscriptionIncomingReceived);
	}else{
		/*subscribe refresh, unhandled*/
	}
661

662 663
}

664
static void incoming_subscribe_closed(SalOp *op){
665
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
666

667 668 669
	linphone_event_set_state(lev,LinphoneSubscriptionTerminated);
}

670
static void on_publish_response(SalOp* op){
671 672
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
	const SalErrorInfo *ei=op->get_error_info();
673

674
	if (lev==NULL) return;
675
	if (ei->reason==SalReasonNone){
676 677
		if (!lev->terminating)
			linphone_event_set_publish_state(lev,LinphonePublishOk);
678
		else
679 680
			linphone_event_set_publish_state(lev,LinphonePublishCleared);
	}else{
Simon Morlat's avatar
Simon Morlat committed
681 682 683 684 685
		if (lev->publish_state==LinphonePublishOk){
			linphone_event_set_publish_state(lev,LinphonePublishProgress);
		}else{
			linphone_event_set_publish_state(lev,LinphonePublishError);
		}
686 687 688
	}
}

689

690
static void on_expire(SalOp *op){
691
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
692

693
	if (lev==NULL) return;
694

695 696
	if (linphone_event_get_publish_state(lev)==LinphonePublishOk){
		linphone_event_set_publish_state(lev,LinphonePublishExpiring);
697 698
	}else if (linphone_event_get_subscription_state(lev)==LinphoneSubscriptionActive){
		linphone_event_set_state(lev,LinphoneSubscriptionExpiring);
699 700 701
	}
}

702
static void on_notify_response(SalOp *op){
703
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
704 705 706 707 708 709

	if (lev==NULL) return;
	/*this is actually handling out of dialogs notify - for the moment*/
	if (!lev->is_out_of_dialog_op) return;
	switch (linphone_event_get_subscription_state(lev)){
		case LinphoneSubscriptionIncomingReceived:
710
			if (op->get_error_info()->reason == SalReasonNone){
711 712 713 714 715 716 717 718 719 720
				linphone_event_set_state(lev, LinphoneSubscriptionTerminated);
			}else{
				linphone_event_set_state(lev, LinphoneSubscriptionError);
			}
		break;
		default:
			ms_warning("Unhandled on_notify_response() case %s", linphone_subscription_state_to_string(linphone_event_get_subscription_state(lev)));
	}
}

721
static void refer_received(SalOp *op, const SalAddress *refer_to){
722 723 724 725
	if (sal_address_has_param(refer_to, "text")) {
		LinphonePrivate::Address addr(sal_address_as_string(refer_to));
		if (addr.isValid()) {
			LinphoneCore *lc = reinterpret_cast<LinphoneCore *>(op->get_sal()->get_user_pointer());
726 727
			if (addr.hasUriParam("method") && (addr.getUriParamValue("method") == "BYE")) {
				// The server asks a participant to leave a chat room
728
				LinphoneChatRoom *cr = L_GET_C_BACK_PTR(lc->cppCore->findChatRoom(addr));
729 730 731 732 733 734 735
				if (cr) {
					L_GET_CPP_PTR_FROM_C_OBJECT(cr)->leave();
					static_cast<SalReferOp *>(op)->reply(SalReasonNone);
					return;
				}
				static_cast<SalReferOp *>(op)->reply(SalReasonDeclined);
			} else if (addr.hasParam("admin")) {
736
				LinphoneChatRoom *cr = L_GET_C_BACK_PTR(lc->cppCore->findChatRoom(Address(op->get_to())));
737
				if (cr) {
738 739 740 741 742 743 744
					Address fromAddr(op->get_from());
					std::shared_ptr<Participant> participant = L_GET_CPP_PTR_FROM_C_OBJECT(cr)->findParticipant(fromAddr);
					if (!participant || !participant->isAdmin()) {
						static_cast<SalReferOp *>(op)->reply(SalReasonDeclined);
						return;
					}
					participant = L_GET_CPP_PTR_FROM_C_OBJECT(cr)->findParticipant(addr);
745 746 747 748 749 750 751 752
					if (participant) {
						bool value = Utils::stob(addr.getParamValue("ad