callbacks.c 29.5 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"
42
#include "chat/chat-message/chat-message-p.h"
Ronan's avatar
Ronan committed
43
#include "chat/chat-room/chat-room.h"
44
#include "chat/chat-room/server-group-chat-room-p.h"
45
#include "conference/participant.h"
46
#include "conference/session/call-session-p.h"
47
#include "conference/session/call-session.h"
48
#include "conference/session/media-session-p.h"
49
#include "conference/session/media-session.h"
50
#include "core/core-p.h"
51

52 53
using namespace std;

54
using namespace LinphonePrivate;
55

56
static void register_failure(SalOp *op);
57

58
static void call_received(SalCallOp *h) {
59
	/* Look if this INVITE is for a call that has already been notified but broken because of network failure */
60
	LinphoneCore *lc = reinterpret_cast<LinphoneCore *>(h->get_sal()->get_user_pointer());
61
	if (L_GET_PRIVATE_FROM_C_OBJECT(lc)->inviteReplacesABrokenCall(h))
62
		return;
63

64
	LinphoneAddress *fromAddr = nullptr;
65
	const char *pAssertedId = sal_custom_header_find(h->get_recv_custom_header(), "P-Asserted-Identity");
66 67 68 69 70
	/* 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) {
71
				ms_message("Using P-Asserted-Identity [%s] instead of from [%s] for op [%p]", pAssertedId, h->get_from(), h);
72 73 74 75 76 77 78 79
				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)
80 81
		fromAddr = linphone_address_new(h->get_from());
	LinphoneAddress *toAddr = linphone_address_new(h->get_to());
82

83 84 85
	if (_linphone_core_is_conference_creation(lc, toAddr)) {
		linphone_address_unref(toAddr);
		linphone_address_unref(fromAddr);
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
		if (sal_address_has_param(h->get_remote_contact_address(), "text")) {
			bool oneToOneChatRoom = false;
			const char *oneToOneChatRoomStr = sal_custom_header_find(h->get_recv_custom_header(), "One-To-One-Chat-Room");
			if (oneToOneChatRoomStr && (strcmp(oneToOneChatRoomStr, "true") == 0))
				oneToOneChatRoom = true;
			if (oneToOneChatRoom) {
				IdentityAddress from(h->get_from());
				list<IdentityAddress> identAddresses = ServerGroupChatRoom::parseResourceLists(h->get_remote_body());
				if (identAddresses.size() != 1) {
					h->decline(SalReasonNotAcceptable, nullptr);
					return;
				}
				IdentityAddress confAddr = L_GET_PRIVATE_FROM_C_OBJECT(lc)->mainDb->findOneToOneConferenceChatRoomAddress(from, identAddresses.front());
				if (confAddr.isValid()) {
					shared_ptr<AbstractChatRoom> chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(ChatRoomId(confAddr, confAddr));
					L_GET_PRIVATE(static_pointer_cast<ServerGroupChatRoom>(chatRoom))->confirmRecreation(h);
					return;
				}
			}
			_linphone_core_create_server_group_chat_room(lc, h);
		}
		// TODO: handle media conference creation if the "text" feature tag is not present
108 109
		return;
	} else if (sal_address_has_param(h->get_remote_contact_address(), "text")) {
110
		shared_ptr<AbstractChatRoom> chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(
111
			ChatRoomId(IdentityAddress(h->get_to()), IdentityAddress(h->get_to()))
112 113 114
		);
		if (chatRoom) {
			L_GET_PRIVATE(static_pointer_cast<ServerGroupChatRoom>(chatRoom))->confirmJoining(h);
115 116
			linphone_address_unref(toAddr);
			linphone_address_unref(fromAddr);
117 118 119
		} else {
			//invite is for an unknown chatroom
			h->decline(SalReasonNotFound, nullptr);
120
		}
121
		return;
122 123 124 125
	} else {
		// TODO: handle media conference joining if the "text" feature tag is not present
	}

126 127 128
	/* First check if we can answer successfully to this invite */
	LinphonePresenceActivity *activity = nullptr;
	if ((linphone_presence_model_get_basic_status(lc->presence_model) == LinphonePresenceBasicStatusClosed)
129
		&& (activity = linphone_presence_model_get_activity(lc->presence_model))) {
130
		char *altContact = nullptr;
131 132
		switch (linphone_presence_activity_get_type(activity)) {
			case LinphonePresenceActivityPermanentAbsence:
133 134
				altContact = linphone_presence_model_get_contact(lc->presence_model);
				if (altContact) {
135 136
					SalErrorInfo sei;
					memset(&sei, 0, sizeof(sei));
137
					sal_error_info_set(&sei, SalReasonRedirect, "SIP", 0, nullptr, nullptr);
138 139
					SalAddress *altAddr = sal_address_new(altContact);
					h->decline_with_error_info(&sei, altAddr);
140
					ms_free(altContact);
141
					sal_address_unref(altAddr);
142 143 144
					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);
145
					h->release();
146
					sal_error_info_reset(&sei);
147
					return;
148
				}
149 150
				break;
			default:
151
				/* Nothing special to be done */
152
				break;
153
		}
Simon Morlat's avatar
Simon Morlat committed
154
	}
155

156
	if (!L_GET_PRIVATE_FROM_C_OBJECT(lc)->canWeAddCall()) { /* Busy */
157
		h->decline(SalReasonBusy, nullptr);
158 159 160
		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);
161
		h->release();
Simon Morlat's avatar
Simon Morlat committed
162 163
		return;
	}
164

165 166
	/* Check if I'm the caller */
	LinphoneAddress *fromAddressToSearchIfMe = nullptr;
167
	if (h->get_privacy() == SalPrivacyNone)
168 169 170 171 172
		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");
173
	if (fromAddressToSearchIfMe && L_GET_PRIVATE_FROM_C_OBJECT(lc)->isAlreadyInCallWithAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(fromAddressToSearchIfMe))) {
174 175
		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);
176
		h->decline(SalReasonBusy, nullptr);
177 178 179
		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);
180
		h->release();
181
		linphone_address_unref(fromAddressToSearchIfMe);
182
		ms_free(addr);
Simon Morlat's avatar
Simon Morlat committed
183 184
		return;
	}
185 186
	if (fromAddressToSearchIfMe)
		linphone_address_unref(fromAddressToSearchIfMe);
187

188 189 190
	LinphoneCall *call = linphone_call_new_incoming(lc, fromAddr, toAddr, h);
	linphone_address_unref(fromAddr);
	linphone_address_unref(toAddr);
191
	L_GET_PRIVATE_FROM_C_OBJECT(call)->startIncomingNotification();
192 193
}

194 195
static void call_rejected(SalCallOp *h){
	LinphoneCore *lc=(LinphoneCore *)h->get_sal()->get_user_pointer();
196 197
	LinphoneErrorInfo *ei = linphone_error_info_new();
	linphone_error_info_from_sal_op(ei, h);
198
	linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, linphone_address_new(h->get_from()), linphone_address_new(h->get_to()), ei);
199 200
}

201
static void call_ringing(SalOp *h) {
202
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(h->get_user_pointer());
203 204
	if (!session) return;
	L_GET_PRIVATE(session)->remoteRinging();
205 206
}

207 208 209 210 211
/*
 * could be reach :
 *  - when the call is accepted
 *  - when a request is accepted (pause, resume)
 */
212
static void call_accepted(SalOp *op) {
213
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
214 215 216
	if (!session) {
		ms_warning("call_accepted: CallSession no longer exists");
		return;
Simon Morlat's avatar
Simon Morlat committed
217
	}
218
	L_GET_PRIVATE(session)->accepted();
219 220
}

221
/* this callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session*/
222
static void call_updating(SalOp *op, bool_t is_update) {
223
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
224 225 226
	if (!session) {
		ms_warning("call_updating: CallSession no longer exists");
		return;
227
	}
228
	L_GET_PRIVATE(session)->updating(!!is_update);
229 230 231
}


232
static void call_ack_received(SalOp *op, SalCustomHeader *ack) {
233
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
234 235
	if (!session) {
		ms_warning("call_ack_received(): no CallSession for which an ack is expected");
236 237
		return;
	}
238
	L_GET_PRIVATE(session)->ackReceived(reinterpret_cast<LinphoneHeaders *>(ack));
239 240
}

241

242
static void call_ack_being_sent(SalOp *op, SalCustomHeader *ack) {
243
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
244 245
	if (!session) {
		ms_warning("call_ack_being_sent(): no CallSession for which an ack is supposed to be sent");
246 247
		return;
	}
248
	L_GET_PRIVATE(session)->ackBeingSent(reinterpret_cast<LinphoneHeaders *>(ack));
249 250
}

251
static void call_terminated(SalOp *op, const char *from) {
252
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
253 254 255
	if (!session)
		return;
	L_GET_PRIVATE(session)->terminated();
256 257
}

258
static void call_failure(SalOp *op) {
259
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
260 261 262
	if (!session) {
		ms_warning("Failure reported on already terminated CallSession");
		return;
263
	}
264
	L_GET_PRIVATE(session)->failure();
265 266
}

267
static void call_released(SalOp *op) {
268
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
269 270 271 272
	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
273
	}
274
	L_GET_PRIVATE(session)->setState(LinphonePrivate::CallSession::State::Released, "Call released");
275 276
}

277
static void call_cancel_done(SalOp *op) {
278 279 280 281
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
	if (!session) {
		ms_warning("Cancel done reported on already terminated CallSession");
		return;
282
	}
283
	L_GET_PRIVATE(session)->cancelDone();
284 285
}

jehan's avatar
jehan committed
286
static void auth_failure(SalOp *op, SalAuthInfo* info) {
287
	LinphoneCore *lc = reinterpret_cast<LinphoneCore *>(op->get_sal()->get_user_pointer());
288
	LinphoneAuthInfo *ai = NULL;
289

290 291
	if (info != NULL) {
		ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, info->realm, info->username, info->domain, TRUE);
292
		if (ai){
293
			LinphoneAuthMethod method = info->mode == SalAuthModeHttpDigest ? LinphoneAuthHttpDigest : LinphoneAuthTls;
294
			LinphoneAuthInfo *auth_info = linphone_core_create_auth_info(lc, info->username, NULL, NULL, NULL, info->realm, info->domain);
295
			ms_message("%s/%s/%s/%s authentication fails.", info->realm, info->username, info->domain, info->mode == SalAuthModeHttpDigest ? "HttpDigest" : "Tls");
296
			/*ask again for password if auth info was already supplied but apparently not working*/
297
			linphone_core_notify_authentication_requested(lc, auth_info, method);
298
			linphone_auth_info_unref(auth_info);
299 300
			// Deprecated
			linphone_core_notify_auth_info_requested(lc, info->realm, info->username, info->domain);
301
		}
jehan's avatar
jehan committed
302
	}
303
}
304

305
static void register_success(SalOp *op, bool_t registered){
306
	LinphoneProxyConfig *cfg=(LinphoneProxyConfig *)op->get_user_pointer();
307 308
	if (!cfg){
		ms_message("Registration success for deleted proxy config, ignored");
309 310
		return;
	}
311
	linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared ,
312
									registered ? "Registration successful" : "Unregistration done");
313 314
}

315
static void register_failure(SalOp *op){
316 317
	LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)op->get_user_pointer();
	const SalErrorInfo *ei=op->get_error_info();
318
	const char *details=ei->full_string;
319 320 321 322 323 324

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

327
	if ((ei->reason == SalReasonServiceUnavailable || ei->reason == SalReasonIOError)
328
			&& linphone_proxy_config_get_state(cfg) == LinphoneRegistrationOk) {
329
		linphone_proxy_config_set_state(cfg,LinphoneRegistrationProgress,"Service unavailable, retrying");
330 331 332
	} else {
		linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details);
	}
333
	if (cfg->presence_publish_event){
334
		/*prevent publish to be sent now until registration gets successful*/
335 336
		linphone_event_terminate(cfg->presence_publish_event);
		cfg->presence_publish_event=NULL;
337 338
		cfg->send_publish=cfg->publish;
	}
339 340
}

341
static void vfu_request(SalOp *op) {
342
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
343 344 345 346 347 348
	if (!session)
		return;
	LinphonePrivate::MediaSession *mediaSession = dynamic_cast<LinphonePrivate::MediaSession *>(session);
	if (!mediaSession) {
		ms_warning("VFU request but no MediaSession!");
		return;
349
	}
350
	L_GET_PRIVATE(mediaSession)->sendVfu();
351 352
}

Ghislain MARY's avatar
Ghislain MARY committed
353 354 355 356 357 358 359 360 361 362
static void dtmf_received(SalOp *op, char dtmf) {
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
	if (!session)
		return;
	LinphonePrivate::MediaSession *mediaSession = dynamic_cast<LinphonePrivate::MediaSession *>(session);
	if (!mediaSession) {
		ms_warning("DTMF received but no MediaSession!");
		return;
	}
	L_GET_PRIVATE(mediaSession)->dtmfReceived(dtmf);
363 364
}

365 366 367 368 369 370 371 372 373 374 375 376
static void call_refer_received(SalOp *op, const SalAddress *referTo) {
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
	char *addrStr = sal_address_as_string_uri_only(referTo);
	Address referToAddr(addrStr);
	string method;
	if (referToAddr.isValid())
		method = referToAddr.getMethodParam();
	if (session && (method.empty() || (method == "INVITE"))) {
		L_GET_PRIVATE(session)->referred(referToAddr);
	} else {
		LinphoneCore *lc = reinterpret_cast<LinphoneCore *>(op->get_sal()->get_user_pointer());
		linphone_core_notify_refer_received(lc, addrStr);
377
	}
378
	bctbx_free(addrStr);
379 380
}

Ghislain MARY's avatar
Ghislain MARY committed
381
static void message_received(SalOp *op, const SalMessage *msg){
382 383
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
	LinphoneCall *call=(LinphoneCall*)op->get_user_pointer();
384
	LinphoneReason reason = lc->chat_deny_code;
385 386
	if (reason == LinphoneReasonNone) {
		linphone_core_message_received(lc, op, msg);
387
	}
388
	auto messageOp = dynamic_cast<SalMessageOpInterface *>(op);
389 390
	messageOp->reply(linphone_reason_to_sal(reason));
	if (!call) op->release();
391 392
}

393
static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) {
394
	linphone_notify_parse_presence(content_type, content_subtype, body, result);
395 396
}

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

400 401 402
	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
403
		linphone_address_unref(presentity);
404 405
	}
	*content = linphone_presence_model_to_xml((LinphonePresenceModel*)presence);
406 407
}

408
static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model, const char *msg){
409
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
410
	linphone_notify_recv(lc,op,ss,model);
411 412
}

413 414
static void subscribe_presence_received(SalPresenceOp *op, const char *from){
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
415
	linphone_subscription_new(lc,op,from);
416 417
}

418 419
static void subscribe_presence_closed(SalPresenceOp *op, const char *from){
	LinphoneCore *lc=(LinphoneCore *)op->get_sal()->get_user_pointer();
420
	linphone_subscription_closed(lc,op);
421 422
}

423
static void ping_reply(SalOp *op) {
424
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
425 426 427
	if (!session) {
		ms_warning("Ping reply without CallSession attached...");
		return;
Simon Morlat's avatar
Simon Morlat committed
428
	}
429
	L_GET_PRIVATE(session)->pingReply();
430
}
jehan's avatar
jehan committed
431

432
static bool_t fill_auth_info_with_client_certificate(LinphoneCore *lc, SalAuthInfo* sai) {
433 434
	const char *chain_file = linphone_core_get_tls_cert_path(lc);
	const char *key_file = linphone_core_get_tls_key_path(lc);
435

436
	if (key_file && chain_file) {
437
#ifndef _WIN32
438 439 440 441 442 443 444 445 446 447
		// 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;
		}
448
#endif
449 450 451 452 453 454
		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
455
	return sai->certificates && sai->key;
456 457
}

jehan's avatar
jehan committed
458
static bool_t fill_auth_info(LinphoneCore *lc, SalAuthInfo* sai) {
459 460 461 462 463 464
	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
465
	if (ai) {
466
		if (sai->mode == SalAuthModeHttpDigest) {
467 468 469 470 471 472 473 474 475 476 477 478
			/*
			 * Compare algorithm of server(sai) with algorithm of client(ai), if they are not correspondant,
			 * exit. The default algorithm is MD5 if it's NULL.
			 */
			if (sai->algorithm && ai->algorithm) {
				if (strcmp(ai->algorithm, sai->algorithm))
					return TRUE;
			} else if (
				(ai->algorithm && strcmp(ai->algorithm, "MD5")) ||
				(sai->algorithm && strcmp(sai->algorithm, "MD5"))
			)
				return TRUE;
479

480 481 482
			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;
483 484 485 486 487 488 489 490 491 492 493
		} 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);
			}
		}
494

495 496 497 498 499
		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);
		}
500 501
		return TRUE;
	} else {
502 503 504
		if (sai->mode == SalAuthModeTls) {
			return fill_auth_info_with_client_certificate(lc, sai);
		}
505 506 507
		return FALSE;
	}
}
jehan's avatar
jehan committed
508
static bool_t auth_requested(Sal* sal, SalAuthInfo* sai) {
509
	LinphoneCore *lc = (LinphoneCore *)sal->get_user_pointer();
510 511 512 513
	if (fill_auth_info(lc,sai)) {
		return TRUE;
	} else {
		LinphoneAuthMethod method = sai->mode == SalAuthModeHttpDigest ? LinphoneAuthHttpDigest : LinphoneAuthTls;
514 515
		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);
516
		linphone_auth_info_unref(ai);
517 518
		// Deprecated
		linphone_core_notify_auth_info_requested(lc, sai->realm, sai->username, sai->domain);
519
		if (fill_auth_info(lc, sai)) {
520
			return TRUE;
jehan's avatar
jehan committed
521 522 523 524
		}
		return FALSE;
	}
}
525

526 527 528 529 530
static void notify_refer(SalOp *op, SalReferStatus status) {
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
	if (!session) {
		ms_warning("Receiving notify_refer for unknown CallSession");
		return;
531
	}
532
	LinphonePrivate::CallSession::State cstate;
533
	switch (status) {
534
		case SalReferTrying:
535
			cstate = LinphonePrivate::CallSession::State::OutgoingProgress;
536
			break;
537
		case SalReferSuccess:
538
			cstate = LinphonePrivate::CallSession::State::Connected;
539
			break;
540 541
		case SalReferFailed:
		default:
542
			cstate = LinphonePrivate::CallSession::State::Error;
543
			break;
544
	}
545
	L_GET_PRIVATE(session)->setTransferState(cstate);
546
	if (cstate == LinphonePrivate::CallSession::State::Connected)
547
		session->terminate(); // Automatically terminate the call as the transfer is complete
548 549
}

Ghislain MARY's avatar
Ghislain MARY committed
550
static LinphoneChatMessageState chatStatusSal2Linphone(SalMessageDeliveryStatus status){
551
	switch(status){
Ghislain MARY's avatar
Ghislain MARY committed
552
		case SalMessageDeliveryInProgress:
553
			return LinphoneChatMessageStateInProgress;
Ghislain MARY's avatar
Ghislain MARY committed
554
		case SalMessageDeliveryDone:
555
			return LinphoneChatMessageStateDelivered;
Ghislain MARY's avatar
Ghislain MARY committed
556
		case SalMessageDeliveryFailed:
557 558 559 560 561
			return LinphoneChatMessageStateNotDelivered;
	}
	return LinphoneChatMessageStateIdle;
}

562 563 564 565 566 567 568
static void message_delivery_update(SalOp *op, SalMessageDeliveryStatus status) {
	LinphonePrivate::ChatMessage *msg = reinterpret_cast<LinphonePrivate::ChatMessage *>(op->get_user_pointer());
	if (!msg)
		return; // Do not handle delivery status for isComposing messages.

	// Check that the message does not belong to an already destroyed chat room - if so, do not invoke callbacks
	if (msg->getChatRoom())
569
		L_GET_PRIVATE(msg)->setState((LinphonePrivate::ChatMessage::State)chatStatusSal2Linphone(status));
570 571
}

572 573 574 575 576
static void info_received(SalOp *op, SalBodyHandler *body_handler) {
	LinphonePrivate::CallSession *session = reinterpret_cast<LinphonePrivate::CallSession *>(op->get_user_pointer());
	if (!session)
		return;
	L_GET_PRIVATE(session)->infoReceived(body_handler);
Simon Morlat's avatar
Simon Morlat committed
577 578
}

579
static void subscribe_response(SalOp *op, SalSubscribeStatus status, int will_retry){
580
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
581

582
	if (lev==NULL) return;
583

584 585 586 587 588
	if (status==SalSubscribeActive){
		linphone_event_set_state(lev,LinphoneSubscriptionActive);
	}else if (status==SalSubscribePending){
		linphone_event_set_state(lev,LinphoneSubscriptionPending);
	}else{
589
		if (will_retry){
590 591 592
			linphone_event_set_state(lev,LinphoneSubscriptionOutgoingProgress);
		}
		else linphone_event_set_state(lev,LinphoneSubscriptionError);
593 594 595
	}
}

596 597 598
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();
599 600
	bool_t out_of_dialog = (lev==NULL);
	if (out_of_dialog) {
601 602
		/*out of dialog notify */
		lev = linphone_event_new_with_out_of_dialog_op(lc,op,LinphoneSubscriptionOutgoing,eventname);
603
	}
604
	{
605
		LinphoneContent *ct=linphone_content_from_sal_body_handler(body_handler);
Ghislain MARY's avatar
Ghislain MARY committed
606 607 608 609
		if (ct) {
			linphone_core_notify_notify_received(lc,lev,eventname,ct);
			linphone_content_unref(ct);
		}
610
	}
611 612 613
	if (out_of_dialog){
		/*out of dialog NOTIFY do not create an implicit subscription*/
		linphone_event_set_state(lev, LinphoneSubscriptionTerminated);
614
	}else if (st!=SalSubscribeNone){
615 616 617 618
		linphone_event_set_state(lev,linphone_subscription_state_from_sal(st));
	}
}

619 620 621
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();
622

623
	if (lev==NULL) {
624
		lev=linphone_event_new_with_op(lc,op,LinphoneSubscriptionIncoming,eventname);
625 626 627 628
		linphone_event_set_state(lev,LinphoneSubscriptionIncomingReceived);
	}else{
		/*subscribe refresh, unhandled*/
	}
629

630 631
}

632
static void incoming_subscribe_closed(SalOp *op){
633
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
634

635 636 637
	linphone_event_set_state(lev,LinphoneSubscriptionTerminated);
}

638
static void on_publish_response(SalOp* op){
639 640
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
	const SalErrorInfo *ei=op->get_error_info();
641

642
	if (lev==NULL) return;
643
	if (ei->reason==SalReasonNone){
644 645
		if (!lev->terminating)
			linphone_event_set_publish_state(lev,LinphonePublishOk);
646
		else
647 648
			linphone_event_set_publish_state(lev,LinphonePublishCleared);
	}else{
649 650 651 652 653
		if (lev->publish_state==LinphonePublishOk){
			linphone_event_set_publish_state(lev,LinphonePublishProgress);
		}else{
			linphone_event_set_publish_state(lev,LinphonePublishError);
		}
654 655 656
	}
}

657

658
static void on_expire(SalOp *op){
659
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
660

661
	if (lev==NULL) return;
662

663 664
	if (linphone_event_get_publish_state(lev)==LinphonePublishOk){
		linphone_event_set_publish_state(lev,LinphonePublishExpiring);
665 666
	}else if (linphone_event_get_subscription_state(lev)==LinphoneSubscriptionActive){
		linphone_event_set_state(lev,LinphoneSubscriptionExpiring);
667 668 669
	}
}

670
static void on_notify_response(SalOp *op){
671
	LinphoneEvent *lev=(LinphoneEvent*)op->get_user_pointer();
672 673
	if (!lev)
		return;
674

675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
	if (lev->is_out_of_dialog_op) {
		switch (linphone_event_get_subscription_state(lev)) {
			case LinphoneSubscriptionIncomingReceived:
				if (op->get_error_info()->reason == SalReasonNone)
					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)));
				break;
		}
	} else {
		ms_warning("on_notify_response in dialog");
		_linphone_event_notify_notify_response(lev);
691 692 693
	}
}

694
static void refer_received(SalOp *op, const SalAddress *refer_to){
695
	if (sal_address_has_param(refer_to, "text")) {
696 697 698
		char *refer_uri = sal_address_as_string(refer_to);
		LinphonePrivate::Address addr(refer_uri);
		bctbx_free(refer_uri);
699 700
		if (addr.isValid()) {
			LinphoneCore *lc = reinterpret_cast<LinphoneCore *>(op->get_sal()->get_user_pointer());
701
			if (addr.hasUriParam("method") && (addr.getUriParamValue("method") == "BYE")) {
702 703
				if (linphone_core_conference_server_enabled(lc)) {
					// Removal of a participant at the server side
704
					shared_ptr<AbstractChatRoom> chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(
705
						ChatRoomId(IdentityAddress(op->get_to()), IdentityAddress(op->get_to()))
706
					);
707
					if (chatRoom) {
708
						std::shared_ptr<Participant> participant = chatRoom->findParticipant(IdentityAddress(op->get_from()));
709 710 711 712
						if (!participant || !participant->isAdmin()) {
							static_cast<SalReferOp *>(op)->reply(SalReasonDeclined);
							return;
						}
713
						participant = chatRoom->findParticipant(addr);
714
						if (participant)
715
							chatRoom->removeParticipant(participant);
716 717 718 719 720
						static_cast<SalReferOp *>(op)->reply(SalReasonNone);
						return;
					}
				} else {
					// The server asks a participant to leave a chat room
721
					LinphoneChatRoom *cr = L_GET_C_BACK_PTR(
722
						L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(ChatRoomId(addr, IdentityAddress(op->get_to())))
723
					);
724 725 726 727 728 729
					if (cr) {
						L_GET_CPP_PTR_FROM_C_OBJECT(cr)->leave();
						static_cast<SalReferOp *>(op)->reply(SalReasonNone);
						return;
					}
					static_cast<SalReferOp *>(op)->reply(SalReasonDeclined);
730 731
				}
			} else if (addr.hasParam("admin")) {
732
				LinphoneChatRoom *cr = L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(
733
					ChatRoomId(IdentityAddress(op->get_to()), IdentityAddress(op->get_to()))
734
				));
735
				if (cr) {
736 737 738 739 740 741 742
					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);
743 744 745 746 747 748 749 750
					if (participant) {
						bool value = Utils::stob(addr.getParamValue("admin"));
						L_GET_CPP_PTR_FROM_C_OBJECT(cr)->setParticipantAdminStatus(participant, value);
					}
					static_cast<SalReferOp *>(op)->reply(SalReasonNone);
					return;
				}
			} else {
751
				LinphoneChatRoom *cr = L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(ChatRoomId(addr, IdentityAddress(op->get_to()))));
752
				if (!cr)
753
					cr = _linphone_client_group_chat_room_new(lc, addr.asString().c_str(), nullptr, FALSE);
754 755 756
				L_GET_CPP_PTR_FROM_C_OBJECT(cr)->join();
				static_cast<SalReferOp *>(op)->reply(SalReasonNone);
				return;
757 758 759 760
			}
		}
	}
	static_cast<SalReferOp *>(op)->reply(SalReasonDeclined);
761 762
}

763
Sal::Callbacks linphone_sal_callbacks={
764
	call_received,
765
	call_rejected,
766 767
	call_ringing,
	call_accepted,
768 769
	call_ack_received,
	call_ack_being_sent,
770
	call_updating,
771 772
	call_terminated,
	call_failure,
773
	call_released,
774
	call_cancel_done,
775
	call_refer_received,
jehan's avatar
jehan committed
776
	auth_failure,
777 778 779 780
	register_success,
	register_failure,
	vfu_request,
	dtmf_received,
Ghislain MARY's avatar
Ghislain MARY committed
781 782
	message_received,
	message_delivery_update,
783
	notify_refer,
784
	subscribe_received,
785
	incoming_subscribe_closed,
786 787 788 789
	subscribe_response,
	notify,
	subscribe_presence_received,
	subscribe_presence_closed,
790
	parse_presence_requested,
791
	convert_presence_to_xml_requested,
792
	notify_presence,
793
	ping_reply,
Simon Morlat's avatar
Simon Morlat committed
794
	auth_requested,
795 796
	info_received,
	on_publish_response,
797
	on_expire,
798 799
	on_notify_response,
	refer_received,
800
};