call-session.cpp 44.5 KB
Newer Older
1 2
/*
 * call-session.cpp
Ronan's avatar
Ronan committed
3
 * Copyright (C) 2010-2018 Belledonne Communications SARL
4
 *
Ghislain MARY's avatar
Ghislain MARY committed
5 6 7 8
 * 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.
9 10 11 12 13 14 15
 *
 * 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
Ghislain MARY's avatar
Ghislain MARY committed
16 17
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 19
 */

20
#include <bctoolbox/defs.h>
21

22 23
#include "linphone/api/c-content.h"
#include "linphone/core.h"
24 25

#include "address/address-p.h"
26
#include "c-wrapper/c-wrapper.h"
27 28
#include "call/call-p.h"
#include "conference/params/call-session-params-p.h"
29
#include "conference/session/call-session-p.h"
30
#include "conference/session/call-session.h"
31
#include "core/core-p.h"
32 33 34 35 36
#include "logger/logger.h"

#include "private.h"

using namespace std;
37 38 39 40 41

LINPHONE_BEGIN_NAMESPACE

// =============================================================================

42 43 44 45 46 47 48 49 50 51 52
int CallSessionPrivate::computeDuration () const {
	if (log->connected_date_time == 0)
		return 0;
	return (int)(ms_time(nullptr) - log->connected_date_time);
}

/*
 * Initialize call parameters according to incoming call parameters. This is to avoid to ask later (during reINVITEs) for features that the remote
 * end apparently does not support. This features are: privacy, video...
 */
void CallSessionPrivate::initializeParamsAccordingToIncomingCallParams () {
53
	currentParams->setPrivacy((LinphonePrivacyMask)op->getPrivacy());
54 55
}

56 57 58
void CallSessionPrivate::notifyReferState () {
	SalCallOp *refererOp = referer->getPrivate()->getOp();
	if (refererOp)
59
		refererOp->notifyReferState(op);
60 61
}

62
void CallSessionPrivate::setState (CallSession::State newState, const string &message) {
63
	L_Q();
64 65
	// Keep a ref on the CallSession, otherwise it might get destroyed before the end of the method
	shared_ptr<CallSession> ref = q->getSharedFromThis();
66 67 68 69 70
	if (state != newState){
		prevState = state;

		/* Make sanity checks with call state changes. Any bad transition can result in unpredictable results
		   or irrecoverable errors in the application. */
71 72 73 74
		if ((state == CallSession::State::End) || (state == CallSession::State::Error)) {
			if (newState != CallSession::State::Released) {
				lFatal() << "Abnormal call resurection from " << Utils::toString(state) <<
					" to " << Utils::toString(newState) << " , aborting";
75 76
				return;
			}
77
		} else if ((newState == CallSession::State::Released) && (prevState != CallSession::State::Error) && (prevState != CallSession::State::End)) {
78 79 80
			lFatal() << "Attempt to move CallSession [" << q << "] to Released state while it was not previously in Error or End state, aborting";
			return;
		}
81
		lInfo() << "CallSession [" << q << "] moving from state " << Utils::toString(state) << " to " << Utils::toString(newState);
82

83 84
		if (newState != CallSession::State::Referred) {
			/* CallSession::State::Referred is rather an event, not a state.
85 86 87 88 89
			   Indeed it does not change the state of the call (still paused or running). */
			state = newState;
		}

		switch (newState) {
90 91
			case CallSession::State::End:
			case CallSession::State::Error:
92 93
				switch (linphone_error_info_get_reason(q->getErrorInfo())) {
					case LinphoneReasonDeclined:
Benjamin REIS's avatar
Benjamin REIS committed
94
						if (log->status != LinphoneCallMissed) // Do not re-change the status of a call if it's already set
95
							log->status = LinphoneCallDeclined;
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
						break;
					case LinphoneReasonNotAnswered:
						if (log->dir == LinphoneCallIncoming)
							log->status = LinphoneCallMissed;
						break;
					case LinphoneReasonNone:
						if (log->dir == LinphoneCallIncoming) {
							if (ei) {
								int code = linphone_error_info_get_protocol_code(ei);
								if ((code >= 200) && (code < 300))
									log->status = LinphoneCallAcceptedElsewhere;
							}
						}
						break;
					case LinphoneReasonDoNotDisturb:
						if (log->dir == LinphoneCallIncoming) {
							if (ei) {
								int code = linphone_error_info_get_protocol_code(ei);
								if ((code >= 600) && (code < 700))
									log->status = LinphoneCallDeclinedElsewhere;
							}
						}
						break;
					default:
						break;
				}
				setTerminated();
				break;
124
			case CallSession::State::Connected:
125 126 127 128 129 130 131 132 133
				log->status = LinphoneCallSuccess;
				log->connected_date_time = ms_time(nullptr);
				break;
			default:
				break;
		}

		if (message.empty()) {
			lError() << "You must fill a reason when changing call state (from " <<
134
				Utils::toString(prevState) << " to " << Utils::toString(state) << ")";
135 136
		}
		if (listener)
137
			listener->onCallSessionStateChanged(q->getSharedFromThis(), newState, message);
138
		if (newState == CallSession::State::Released)
139 140 141 142
			setReleased(); /* Shall be performed after app notification */
	}
}

143
void CallSessionPrivate::setTransferState (CallSession::State newState) {
144 145 146 147
	L_Q();
	if (newState == transferState)
		return;
	lInfo() << "Transfer state for CallSession [" << q << "] changed from ["
148
		<< Utils::toString(transferState) << "] to [" << Utils::toString(newState) << "]";
149 150 151 152 153
	transferState = newState;
	if (listener)
		listener->onCallSessionTransferStateChanged(q->getSharedFromThis(), newState);
}

Ghislain MARY's avatar
Ghislain MARY committed
154 155 156 157 158
void CallSessionPrivate::startIncomingNotification () {
	L_Q();
	if (listener)
		listener->onIncomingCallSessionStarted(q->getSharedFromThis());

159
	setState(CallSession::State::IncomingReceived, "Incoming CallSession");
Ghislain MARY's avatar
Ghislain MARY committed
160

161 162 163 164
	// From now on, the application is aware of the call and supposed to take background task or already submitted
	// notification to the user. We can then drop our background task.
	if (listener)
		listener->onBackgroundTaskToBeStopped(q->getSharedFromThis());
Ghislain MARY's avatar
Ghislain MARY committed
165

166
	if (state == CallSession::State::IncomingReceived) {
Ghislain MARY's avatar
Ghislain MARY committed
167 168 169 170
		handleIncomingReceivedStateInIncomingNotification();
	}
}

171
bool CallSessionPrivate::startPing () {
172 173
	L_Q();
	if (q->getCore()->getCCore()->sip_conf.ping_with_options) {
174 175 176 177
		/* Defer the start of the call after the OPTIONS ping for outgoing call or
		 * send an option request back to the caller so that we get a chance to discover our nat'd address
		 * before answering for incoming call */
		pingReplied = false;
178
		pingOp = new SalOp(q->getCore()->getCCore()->sal);
179
		if (direction == LinphoneCallIncoming) {
180 181
			string from = pingOp->getFrom();
			string to = pingOp->getTo();
182
			linphone_configure_op(q->getCore()->getCCore(), pingOp, log->from, nullptr, false);
183
			pingOp->setRoute(op->getNetworkOrigin());
184
			pingOp->ping(from.c_str(), to.c_str());
185 186 187
		} else if (direction == LinphoneCallOutgoing) {
			char *from = linphone_address_as_string(log->from);
			char *to = linphone_address_as_string(log->to);
188
			pingOp->ping(from, to);
189 190 191
			ms_free(from);
			ms_free(to);
		}
192
		pingOp->setUserPointer(this);
193 194 195 196 197 198 199
		return true;
	}
	return false;
}

// -----------------------------------------------------------------------------

200 201 202 203 204 205
void CallSessionPrivate::setParams (CallSessionParams *csp) {
	if (params)
		delete params;
	params = csp;
}

Ghislain MARY's avatar
Ghislain MARY committed
206 207 208 209
void CallSessionPrivate::createOp () {
	createOpTo(log->to);
}

Ghislain MARY's avatar
Ghislain MARY committed
210 211 212 213
bool CallSessionPrivate::isInConference () const {
	return params->getPrivate()->getInConference();
}

214 215
// -----------------------------------------------------------------------------

216
void CallSessionPrivate::abort (const string &errorMsg) {
217
	op->terminate();
218
	setState(CallSession::State::Error, errorMsg);
219 220
}

221 222 223
void CallSessionPrivate::accepted () {
	/* Immediately notify the connected state, even if errors occur after */
	switch (state) {
224 225 226
		case CallSession::State::OutgoingProgress:
		case CallSession::State::OutgoingRinging:
		case CallSession::State::OutgoingEarlyMedia:
227
			/* Immediately notify the connected state */
228
			setState(CallSession::State::Connected, "Connected");
229 230 231 232
			break;
		default:
			break;
	}
233
	currentParams->setPrivacy((LinphonePrivacyMask)op->getPrivacy());
234 235 236
}

void CallSessionPrivate::ackBeingSent (LinphoneHeaders *headers) {
237
	L_Q();
238
	if (listener)
239
		listener->onAckBeingSent(q->getSharedFromThis(), headers);
240 241 242
}

void CallSessionPrivate::ackReceived (LinphoneHeaders *headers) {
243
	L_Q();
244
	if (listener)
245
		listener->onAckReceived(q->getSharedFromThis(), headers);
246 247
}

248 249 250 251 252 253 254
void CallSessionPrivate::cancelDone () {
	if (reinviteOnCancelResponseRequested) {
		reinviteOnCancelResponseRequested = false;
		reinviteToRecoverFromConnectionLoss();
	}
}

255
bool CallSessionPrivate::failure () {
256
	L_Q();
257
	const SalErrorInfo *ei = op->getErrorInfo();
258 259
	switch (ei->reason) {
		case SalReasonRedirect:
260 261
			if ((state == CallSession::State::OutgoingInit) || (state == CallSession::State::OutgoingProgress)
				|| (state == CallSession::State::OutgoingRinging) /* Push notification case */ || (state == CallSession::State::OutgoingEarlyMedia)) {
262
				const SalAddress *redirectionTo = op->getRemoteContactAddress();
263 264 265 266 267 268 269
				if (redirectionTo) {
					char *url = sal_address_as_string(redirectionTo);
					lWarning() << "Redirecting CallSession [" << q << "] to " << url;
					if (log->to)
						linphone_address_unref(log->to);
					log->to = linphone_address_new(url);
					ms_free(url);
270
					restartInvite();
271 272 273 274 275 276 277 278 279 280
					return true;
				}
			}
			break;
		default:
			break;
	}

	/* Some call errors are not fatal */
	switch (state) {
281 282 283
		case CallSession::State::Updating:
		case CallSession::State::Pausing:
		case CallSession::State::Resuming:
284
			if (ei->reason != SalReasonNoMatch) {
285
				lInfo() << "Call error on state [" << Utils::toString(state) << "], restoring previous state [" << Utils::toString(prevState) << "]";
286 287 288 289 290 291 292
				setState(prevState, ei->full_string);
				return true;
			}
		default:
			break;
	}

293
	if ((state != CallSession::State::End) && (state != CallSession::State::Error)) {
294
		if (ei->reason == SalReasonDeclined)
295
			setState(CallSession::State::End, "Call declined");
296
		else {
297 298
			if (CallSession::isEarlyState(state))
				setState(CallSession::State::Error, ei->full_string ? ei->full_string : "");
299
			else
300
				setState(CallSession::State::End, ei->full_string ? ei->full_string : "");
301
		}
302 303
		if ((ei->reason != SalReasonNone) && listener)
			listener->onPlayErrorTone(q->getSharedFromThis(), linphone_reason_from_sal(ei->reason));
304
	}
305 306 307
	if (referer) {
		// Notify referer of the failure
		notifyReferState();
308 309 310 311
	}
	return false;
}

312 313
void CallSessionPrivate::infoReceived (SalBodyHandler *bodyHandler) {
	L_Q();
314
	LinphoneInfoMessage *info = linphone_core_create_info_message(q->getCore()->getCCore());
315
	linphone_info_message_set_headers(info, op->getRecvCustomHeaders());
316 317 318 319 320 321 322 323 324 325
	if (bodyHandler) {
		LinphoneContent *content = linphone_content_from_sal_body_handler(bodyHandler);
		linphone_info_message_set_content(info, content);
		linphone_content_unref(content);
	}
	if (listener)
		listener->onInfoReceived(q->getSharedFromThis(), info);
	linphone_info_message_unref(info);
}

326
void CallSessionPrivate::pingReply () {
327
	L_Q();
328
	if (state == CallSession::State::OutgoingInit) {
329 330
		pingReplied = true;
		if (isReadyForInvite())
331
			q->startInvite(nullptr, "");
332 333 334
	}
}

335 336 337 338
void CallSessionPrivate::referred (const Address &referToAddr) {
	L_Q();
	referTo = referToAddr.asString();
	referPending = true;
339
	setState(CallSession::State::Referred, "Referred");
340 341 342 343
	if (referPending && listener)
		listener->onCallSessionStartReferred(q->getSharedFromThis());
}

344
void CallSessionPrivate::remoteRinging () {
345
	L_Q();
346
	/* Set privacy */
347
	currentParams->setPrivacy((LinphonePrivacyMask)op->getPrivacy());
348 349
	if (listener)
		listener->onStartRinging(q->getSharedFromThis());
350
	lInfo() << "Remote ringing...";
351
	setState(CallSession::State::OutgoingRinging, "Remote ringing");
352 353
}

354 355 356
void CallSessionPrivate::replaceOp (SalCallOp *newOp) {
	L_Q();
	SalCallOp *oldOp = op;
357
	CallSession::State oldState = state;
358
	op = newOp;
359 360
	op->setUserPointer(q);
	op->setLocalMediaDescription(oldOp->getLocalMediaDescription());
361
	switch (state) {
362 363
		case CallSession::State::IncomingEarlyMedia:
		case CallSession::State::IncomingReceived:
364
			op->notifyRinging((state == CallSession::State::IncomingEarlyMedia) ? true : false);
365
			break;
366 367
		case CallSession::State::Connected:
		case CallSession::State::StreamsRunning:
368 369 370
			op->accept();
			break;
		default:
371
			lWarning() << "CallSessionPrivate::replaceOp(): don't know what to do in state [" << Utils::toString(state) << "]";
372 373 374
			break;
	}
	switch (oldState) {
375 376
		case CallSession::State::IncomingEarlyMedia:
		case CallSession::State::IncomingReceived:
377
			oldOp->setUserPointer(nullptr); // In order for the call session to not get terminated by terminating this op
378
			// Do not terminate a forked INVITE
379
			if (op->getReplaces())
380 381
				oldOp->terminate();
			else
382
				oldOp->killDialog();
383
			break;
384 385
		case CallSession::State::Connected:
		case CallSession::State::StreamsRunning:
386
			oldOp->terminate();
387
			oldOp->killDialog();
388 389 390 391 392 393 394
			break;
		default:
			break;
	}
	oldOp->release();
}

395
void CallSessionPrivate::terminated () {
396
	L_Q();
397
	switch (state) {
398 399
		case CallSession::State::End:
		case CallSession::State::Error:
400 401
			lWarning() << "terminated: already terminated, ignoring";
			return;
402 403
		case CallSession::State::IncomingReceived:
		case CallSession::State::IncomingEarlyMedia:
404
			if (!op->getReasonErrorInfo()->protocol || strcmp(op->getReasonErrorInfo()->protocol, "") == 0) {
405 406 407 408 409 410 411
				linphone_error_info_set(ei, nullptr, LinphoneReasonNotAnswered, 0, "Incoming call cancelled", nullptr);
				nonOpError = true;
			}
			break;
		default:
			break;
	}
412 413
	if (referPending && listener)
		listener->onCallSessionStartReferred(q->getSharedFromThis());
414 415
	if (listener)
		listener->onStopRingingIfInCall(q->getSharedFromThis());
416
	setState(CallSession::State::End, "Call ended");
417 418 419
}

void CallSessionPrivate::updated (bool isUpdate) {
420 421
	L_Q();
	deferUpdate = !!lp_config_get_int(linphone_core_get_config(q->getCore()->getCCore()), "sip", "defer_update_default", FALSE);
422 423 424
	SalErrorInfo sei;
	memset(&sei, 0, sizeof(sei));
	switch (state) {
425
		case CallSession::State::PausedByRemote:
426 427 428
			updatedByRemote();
			break;
		/* SIP UPDATE CASE */
429 430 431
		case CallSession::State::OutgoingRinging:
		case CallSession::State::OutgoingEarlyMedia:
		case CallSession::State::IncomingEarlyMedia:
432
			if (isUpdate) {
433 434
				setState(CallSession::State::EarlyUpdatedByRemote, "EarlyUpdatedByRemote");
				acceptUpdate(nullptr, prevState, Utils::toString(prevState));
435 436
			}
			break;
437 438 439
		case CallSession::State::StreamsRunning:
		case CallSession::State::Connected:
		case CallSession::State::UpdatedByRemote: /* Can happen on UAC connectivity loss */
440 441
			updatedByRemote();
			break;
442
		case CallSession::State::Paused:
443
			/* We'll remain in pause state but accept the offer anyway according to default parameters */
444
			acceptUpdate(nullptr, state, Utils::toString(state));
445
			break;
446 447 448
		case CallSession::State::Updating:
		case CallSession::State::Pausing:
		case CallSession::State::Resuming:
449
			sal_error_info_set(&sei, SalReasonInternalError, "SIP", 0, nullptr, nullptr);
450
			op->declineWithErrorInfo(&sei, nullptr);
451
			BCTBX_NO_BREAK; /* no break */
452 453 454 455 456 457 458 459 460 461 462
		case CallSession::State::Idle:
		case CallSession::State::OutgoingInit:
		case CallSession::State::End:
		case CallSession::State::IncomingReceived:
		case CallSession::State::OutgoingProgress:
		case CallSession::State::Referred:
		case CallSession::State::Error:
		case CallSession::State::Released:
		case CallSession::State::EarlyUpdatedByRemote:
		case CallSession::State::EarlyUpdating:
			lWarning() << "Receiving reINVITE or UPDATE while in state [" << Utils::toString(state) << "], should not happen";
463 464 465 466 467
		break;
	}
}

void CallSessionPrivate::updatedByRemote () {
468
	L_Q();
469
	setState(CallSession::State::UpdatedByRemote,"Call updated by remote");
470 471 472
	if (deferUpdate || deferUpdateInternal) {
		if (state == CallSession::State::UpdatedByRemote && !deferUpdateInternal){
			lInfo() << "CallSession [" << q << "]: UpdatedByRemoted was signaled but defered. LinphoneCore expects the application to call linphone_call_accept_update() later";
473
		}
474
	} else {
475
		if (state == CallSession::State::UpdatedByRemote)
476 477
			q->acceptUpdate(nullptr);
		else {
478 479
			// Otherwise it means that the app responded by CallSession::acceptUpdate() within the callback,
			// so job is already done
480 481 482 483 484 485 486 487 488 489
		}
	}
}

void CallSessionPrivate::updating (bool isUpdate) {
	updated(isUpdate);
}

// -----------------------------------------------------------------------------

490 491 492 493 494 495 496
void CallSessionPrivate::init () {
	currentParams = new CallSessionParams();
	ei = linphone_error_info_new();
}

// -----------------------------------------------------------------------------

497
void CallSessionPrivate::accept (const CallSessionParams *csp) {
498
	L_Q();
499 500
	/* Try to be best-effort in giving real local or routable contact address */
	setContactOp();
501 502 503
	if (csp)
		setParams(new CallSessionParams(*csp));
	if (params)
504
		op->setSentCustomHeaders(params->getPrivate()->getCustomHeaders());
505

506
	op->accept();
507
	if (listener)
508
		listener->onSetCurrentSession(q->getSharedFromThis());
509
	setState(CallSession::State::Connected, "Connected");
510 511
}

512
LinphoneStatus CallSessionPrivate::acceptUpdate (const CallSessionParams *csp, CallSession::State nextState, const string &stateInfo) {
513 514 515
	return startAcceptUpdate(nextState, stateInfo);
}

516
LinphoneStatus CallSessionPrivate::checkForAcceptation () {
517
	L_Q();
518
	switch (state) {
519 520
		case CallSession::State::IncomingReceived:
		case CallSession::State::IncomingEarlyMedia:
521 522
			break;
		default:
523
			lError() << "checkForAcceptation() CallSession [" << q << "] is in state [" << Utils::toString(state) << "], operation not permitted";
524 525
			return -1;
	}
526
	if (listener)
527
		listener->onCheckForAcceptation(q->getSharedFromThis());
528 529

	/* Check if this call is supposed to replace an already running one */
530
	SalOp *replaced = op->getReplaces();
531
	if (replaced) {
532
		CallSession *session = reinterpret_cast<CallSession *>(replaced->getUserPointer());
533 534 535 536 537 538 539 540 541
		if (session) {
			lInfo() << "CallSession " << q << " replaces CallSession " << session << ". This last one is going to be terminated automatically";
			session->terminate();
		}
	}
	return 0;
}

void CallSessionPrivate::handleIncomingReceivedStateInIncomingNotification () {
542
	L_Q();
543 544
	/* Try to be best-effort in giving real local or routable contact address for 100Rel case */
	setContactOp();
545
	if (notifyRinging)
546 547
		op->notifyRinging(false);
	if (op->getReplaces() && lp_config_get_int(linphone_core_get_config(q->getCore()->getCCore()), "sip", "auto_answer_replacing_calls", 1))
548 549 550 551 552 553 554 555 556 557 558 559 560
		q->accept();
}

bool CallSessionPrivate::isReadyForInvite () const {
	bool pingReady = false;
	if (pingOp) {
		if (pingReplied)
			pingReady = true;
	} else
		pingReady = true;
	return pingReady;
}

561
bool CallSessionPrivate::isUpdateAllowed (CallSession::State &nextState) const {
562
	switch (state) {
563 564 565 566 567
		case CallSession::State::IncomingReceived:
		case CallSession::State::IncomingEarlyMedia:
		case CallSession::State::OutgoingRinging:
		case CallSession::State::OutgoingEarlyMedia:
			nextState = CallSession::State::EarlyUpdating;
568
			break;
569 570 571 572 573
		case CallSession::State::Connected:
		case CallSession::State::StreamsRunning:
		case CallSession::State::PausedByRemote:
		case CallSession::State::UpdatedByRemote:
			nextState = CallSession::State::Updating;
574
			break;
575 576
		case CallSession::State::Paused:
			nextState = CallSession::State::Pausing;
577
			break;
578 579 580 581
		case CallSession::State::OutgoingProgress:
		case CallSession::State::Pausing:
		case CallSession::State::Resuming:
		case CallSession::State::Updating:
582 583 584
			nextState = state;
			break;
		default:
585
			lError() << "Update is not allowed in [" << Utils::toString(state) << "] state";
586 587 588 589 590
			return false;
	}
	return true;
}

591 592 593 594 595 596
int CallSessionPrivate::restartInvite () {
	L_Q();
	createOp();
	return q->startInvite(nullptr, subject);
}

597 598 599 600
/*
 * Called internally when reaching the Released state, to perform cleanups to break circular references.
**/
void CallSessionPrivate::setReleased () {
601
	L_Q();
602 603 604 605 606
	if (op) {
		/* Transfer the last error so that it can be obtained even in Released state */
		if (!nonOpError)
			linphone_error_info_from_sal_op(ei, op);
		/* So that we cannot have anymore upcalls for SAL concerning this call */
607
		op->release();
608 609
		op = nullptr;
	}
610 611
	referer = nullptr;
	transferTarget = nullptr;
612

613
	if (listener)
614
		listener->onCallSessionSetReleased(q->getSharedFromThis());
615 616 617 618 619 620 621 622
}

/* This method is called internally to get rid of a call that was notified to the application,
 * because it reached the end or error state. It performs the following tasks:
 * - remove the call from the internal list of calls
 * - update the call logs accordingly
 */
void CallSessionPrivate::setTerminated() {
623
	L_Q();
624 625
	completeLog();
	if (listener)
626
		listener->onCallSessionSetTerminated(q->getSharedFromThis());
627 628
}

629
LinphoneStatus CallSessionPrivate::startAcceptUpdate (CallSession::State nextState, const std::string &stateInfo) {
630
	op->accept();
631 632 633 634
	setState(nextState, stateInfo);
	return 0;
}

635
LinphoneStatus CallSessionPrivate::startUpdate (const string &subject) {
636
	L_Q();
637 638 639 640 641 642 643 644 645 646 647
	string newSubject(subject);
	if (newSubject.empty()) {
		if (q->getParams()->getPrivate()->getInConference())
			newSubject = "Conference";
		else if (q->getParams()->getPrivate()->getInternalCallUpdate())
			newSubject = "ICE processing concluded";
		else if (q->getParams()->getPrivate()->getNoUserConsent())
			newSubject = "Refreshing";
		else
			newSubject = "Media change";
	}
648 649
	if (destProxy && destProxy->op) {
		/* Give a chance to update the contact address if connectivity has changed */
650
		op->setContactAddress(destProxy->op->getContactAddress());
651
	} else
652
		op->setContactAddress(nullptr);
653
	return op->update(newSubject.c_str(), q->getParams()->getPrivate()->getNoUserConsent());
654 655 656
}

void CallSessionPrivate::terminate () {
657
	if ((state == CallSession::State::IncomingReceived) && (linphone_error_info_get_reason(ei) != LinphoneReasonNotAnswered)) {
658 659 660
		linphone_error_info_set_reason(ei, LinphoneReasonDeclined);
		nonOpError = true;
	}
661
	setState(CallSession::State::End, "Call terminated");
662 663
}

664
void CallSessionPrivate::updateCurrentParams () const {}
665 666 667

// -----------------------------------------------------------------------------

Ghislain MARY's avatar
Ghislain MARY committed
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
void CallSessionPrivate::setBroken () {
	switch (state) {
		// For all the early states, we prefer to drop the call
		case CallSession::State::OutgoingInit:
		case CallSession::State::OutgoingProgress:
		case CallSession::State::OutgoingRinging:
		case CallSession::State::OutgoingEarlyMedia:
		case CallSession::State::IncomingReceived:
		case CallSession::State::IncomingEarlyMedia:
			// During the early states, the SAL layer reports the failure from the dialog or transaction layer,
			// hence, there is nothing special to do
		case CallSession::State::StreamsRunning:
		case CallSession::State::Updating:
		case CallSession::State::Pausing:
		case CallSession::State::Resuming:
		case CallSession::State::Paused:
		case CallSession::State::PausedByRemote:
		case CallSession::State::UpdatedByRemote:
			// During these states, the dialog is established. A failure of a transaction is not expected to close it.
			// Instead we have to repair the dialog by sending a reINVITE
			broken = true;
			needLocalIpRefresh = true;
			break;
		default:
			lError() << "CallSessionPrivate::setBroken(): unimplemented case";
			break;
	}
}

697
void CallSessionPrivate::setContactOp () {
698
	L_Q();
699 700 701
	SalAddress *salAddress = nullptr;
	LinphoneAddress *contact = getFixedContact();
	if (contact) {
702 703 704
		auto contactParams = q->getParams()->getPrivate()->getCustomContactParameters();
		for (auto it = contactParams.begin(); it != contactParams.end(); it++)
			linphone_address_set_param(contact, it->first.c_str(), it->second.empty() ? nullptr : it->second.c_str());
705
		salAddress = const_cast<SalAddress *>(L_GET_PRIVATE_FROM_C_OBJECT(contact)->getInternalAddress());
706
		op->setContactAddress(salAddress);
Ghislain MARY's avatar
Ghislain MARY committed
707
		linphone_address_unref(contact);
708 709 710 711 712
	}
}

// -----------------------------------------------------------------------------

Ghislain MARY's avatar
Ghislain MARY committed
713 714
void CallSessionPrivate::onNetworkReachable (bool sipNetworkReachable, bool mediaNetworkReachable) {
	if (sipNetworkReachable)
715
		repairIfBroken();
Ghislain MARY's avatar
Ghislain MARY committed
716 717
	else
		setBroken();
718 719 720 721 722 723 724 725
}

void CallSessionPrivate::onRegistrationStateChanged (LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const std::string &message) {
	repairIfBroken();
}

// -----------------------------------------------------------------------------

726
void CallSessionPrivate::completeLog () {
727
	L_Q();
728 729
	log->duration = computeDuration(); /* Store duration since connected */
	log->error_info = linphone_error_info_ref(ei);
730
	if (log->status == LinphoneCallMissed)
731 732
		q->getCore()->getCCore()->missed_calls++;
	linphone_core_report_call_log(q->getCore()->getCCore(), log);
733 734 735
}

void CallSessionPrivate::createOpTo (const LinphoneAddress *to) {
736
	L_Q();
737
	if (op)
738
		op->release();
739
	op = new SalCallOp(q->getCore()->getCCore()->sal);
740
	op->setUserPointer(q);
741
	if (params->getPrivate()->getReferer())
742
		op->setReferrer(params->getPrivate()->getReferer()->getPrivate()->getOp());
743
	linphone_configure_op(q->getCore()->getCCore(), op, to, q->getParams()->getPrivate()->getCustomHeaders(), false);
744
	if (q->getParams()->getPrivacy() != LinphonePrivacyDefault)
745
		op->setPrivacy((SalPrivacyMask)q->getParams()->getPrivacy());
746 747 748 749 750 751
	/* else privacy might be set by proxy */
}

// -----------------------------------------------------------------------------

LinphoneAddress * CallSessionPrivate::getFixedContact () const {
752
	L_Q();
753
	LinphoneAddress *result = nullptr;
754
	if (op && op->getContactAddress()) {
755 756
		/* If already choosed, don't change it */
		return nullptr;
757
	} else if (pingOp && pingOp->getContactAddress()) {
758 759
		/* If the ping OPTIONS request succeeded use the contact guessed from the received, rport */
		lInfo() << "Contact has been fixed using OPTIONS";
760
		char *addr = sal_address_as_string(pingOp->getContactAddress());
761 762
		result = linphone_address_new(addr);
		ms_free(addr);
763
	} else if (destProxy && destProxy->op && linphone_proxy_config_get_contact(destProxy)) {
764 765
		/* If using a proxy, use the contact address as guessed with the REGISTERs */
		lInfo() << "Contact has been fixed using proxy";
766
		result = linphone_address_clone(linphone_proxy_config_get_contact(destProxy));
767
	} else {
768
		result = linphone_core_get_primary_contact_parsed(q->getCore()->getCCore());
769 770 771 772 773 774 775 776 777 778
		if (result) {
			/* Otherwise use supplied localip */
			linphone_address_set_domain(result, nullptr /* localip */);
			linphone_address_set_port(result, -1 /* linphone_core_get_sip_port(core) */);
			lInfo() << "Contact has not been fixed, stack will do";
		}
	}
	return result;
}

779 780 781 782 783 784 785 786 787 788
// -----------------------------------------------------------------------------

void CallSessionPrivate::reinviteToRecoverFromConnectionLoss () {
	L_Q();
	lInfo() << "CallSession [" << q << "] is going to be updated (reINVITE) in order to recover from lost connectivity";
	q->update(params);
}

void CallSessionPrivate::repairByInviteWithReplaces () {
	L_Q();
789
	lInfo() << "CallSession [" << q << "] is going to have a new INVITE replacing the previous one in order to recover from lost connectivity";
790
	string callId = op->getCallId();
791 792 793
	const char *fromTag = op->getLocalTag();
	const char *toTag = op->getRemoteTag();
	op->killDialog();
794
	createOp();
795
	op->setReplaces(callId.c_str(), fromTag, toTag);
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
	q->startInvite(nullptr);
}

void CallSessionPrivate::repairIfBroken () {
	L_Q();
	LinphoneCore *lc = q->getCore()->getCCore();
	LinphoneConfig *config = linphone_core_get_config(lc);
	if (!lp_config_get_int(config, "sip", "repair_broken_calls", 1) || !lc->media_network_reachable || !broken)
		return;

	// If we are registered and this session has been broken due to a past network disconnection,
	// attempt to repair it

	// Make sure that the proxy from which we received this call, or to which we routed this call is registered first
	if (destProxy) {
		// In all other cases, ie no proxy config, or a proxy config for which no registration was requested,
		// we can start the call session repair immediately.
		if (linphone_proxy_config_register_enabled(destProxy)
			&& (linphone_proxy_config_get_state(destProxy) != LinphoneRegistrationOk))
			return;
	}

	SalErrorInfo sei;
	memset(&sei, 0, sizeof(sei));
	switch (state) {
821 822
		case CallSession::State::Updating:
		case CallSession::State::Pausing:
823
			if (op->dialogRequestPending()) {
824
				// Need to cancel first re-INVITE as described in section 5.5 of RFC 6141
825
				if (op->cancelInvite() == 0){
826 827
					reinviteOnCancelResponseRequested = true;
				}
828 829
			}
			break;
830 831 832
		case CallSession::State::StreamsRunning:
		case CallSession::State::Paused:
		case CallSession::State::PausedByRemote:
833
			if (!op->dialogRequestPending())
834 835
				reinviteToRecoverFromConnectionLoss();
			break;
836
		case CallSession::State::UpdatedByRemote:
837
			if (op->dialogRequestPending()) {
838
				sal_error_info_set(&sei, SalReasonServiceUnavailable, "SIP", 0, nullptr, nullptr);
839
				op->declineWithErrorInfo(&sei, nullptr);
840 841 842
			}
			reinviteToRecoverFromConnectionLoss();
			break;
843 844
		case CallSession::State::OutgoingInit:
		case CallSession::State::OutgoingProgress:
845
			if (op->cancelInvite() == 0){
846 847
				reinviteOnCancelResponseRequested = true;
			}
848
			break;
849 850
		case CallSession::State::OutgoingEarlyMedia:
		case CallSession::State::OutgoingRinging:
851 852
			repairByInviteWithReplaces();
			break;
853 854
		case CallSession::State::IncomingEarlyMedia:
		case CallSession::State::IncomingReceived:
855 856 857
			// Keep the call broken until a forked INVITE is received from the server
			break;
		default:
858
			lWarning() << "CallSessionPrivate::repairIfBroken: don't know what to do in state [" << Utils::toString(state);
859 860 861 862 863 864
			broken = false;
			break;
	}
	sal_error_info_reset(&sei);
}

865 866
// =============================================================================

867 868 869
CallSession::CallSession (const shared_ptr<Core> &core, const CallSessionParams *params, CallSessionListener *listener)
	: Object(*new CallSessionPrivate), CoreAccessor(core) {
	L_D();
870
	getCore()->getPrivate()->registerListener(d);
871 872 873 874
	d->listener = listener;
	if (params)
		d->setParams(new CallSessionParams(*params));
	d->init();
875 876 877
	lInfo() << "New CallSession [" << this << "] initialized (LinphoneCore version: " << linphone_core_get_version() << ")";
}

878 879
CallSession::CallSession (CallSessionPrivate &p, const shared_ptr<Core> &core) : Object(p), CoreAccessor(core) {