liblinphone icon indicating copy to clipboard operation
liblinphone copied to clipboard

Requesting chat room of call results in a crash

Open mbgonicus opened this issue 3 years ago • 0 comments

I am facing a crash (exception) when trying to access the ChatRoom object of a Call via C++ bindings:

void myFunction() {
    call->getChatRoom();
}

The call itself is working correctly and in a good state (i.e. is not terminated, aborted or any of that). This is the stack trace:

 1 raise
 2 abort
 3 __gnu_cxx::__verbose_terminat_handler() [clonde.cold]
 4 __cxxabiv1::___terminate(void ( *)())
 5 std::terminate()
 6 __cxa_throw
 7 std::__throw_logic_error(const char *)
 8 std::string::basic_string(const char *, std::allocator<char> const&) [clone.constprop.0]
 9 LinphonePrivate::Call::getChatRoom()
10 linphone_call_get_chat_room
11 linphone::Call::getChatRoom()
12 myFunction

This is the exception output:

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_M_construct null not valid

Following the stack trace, we figured out that in call.cpp the callId is NULL:

shared_ptr<AbstractChatRoom> Call::getChatRoom () {
	if ((getState() != CallSession::State::End) && (getState() != CallSession::State::Released)) {
		mChatRoom = getCore()->getOrCreateBasicChatRoom(getLocalAddress(), *getRemoteAddress());
		if (mChatRoom) {
			const char *callId = linphone_call_log_get_call_id(getLog());
			lInfo() << "Setting call id [" << callId << "] to ChatRoom [" << mChatRoom << "]";
			mChatRoom->getPrivate()->setCallId(callId);
		}
	}
	return mChatRoom;
}

Thus, when accessing callId in the lInfo line, it causes the exception and breaks. The stdout shows:

"Setting call id ["

because it breaks while reading the callId.

So far for the error.

We also tried to figure out how the callId is set into the CallLog (getLog() in the snipped above retrieves the CallLog from the Call object). We are using linphone::Core::inviteAddressWithParams, which calls linphone_core_invite_address_with_params which then calls linphone_call_new_outgoing:

LinphoneCall *linphone_call_new_outgoing (LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to, const LinphoneCallParams *params, LinphoneProxyConfig *cfg) {
	LinphoneCall *lcall = Call::createCObject(L_GET_CPP_PTR_FROM_C_OBJECT(lc), LinphoneCallOutgoing,
	*L_GET_CPP_PTR_FROM_C_OBJECT(from), *L_GET_CPP_PTR_FROM_C_OBJECT(to),
											  cfg, nullptr, L_GET_CPP_PTR_FROM_C_OBJECT(params));
	
	return lcall;
}

Please note that the second to last argument of the constructor call is nullptr. This parameter is SalCallOp *op:

Call::Call (
	shared_ptr<Core> core,
	LinphoneCallDir direction,
	const Address &from,
	const Address &to,
	LinphoneProxyConfig *cfg,
	SalCallOp *op,
	const MediaSessionParams *msp
) : CoreAccessor(core) {
	mNextVideoFrameDecoded._func = nullptr;
	mNextVideoFrameDecoded._user_data = nullptr;

	mBgTask.setName("Liblinphone call notification");

	//create session
	mParticipant = Participant::create(nullptr, IdentityAddress((direction == LinphoneCallIncoming) ? to : from));
	mParticipant->createSession(getCore(), msp, TRUE, this);
	mParticipant->getSession()->configure(direction, cfg, op, from, to);
}

In the last line of the constructor, the session's configure function is called with the same argument (op = nullptr):

mParticipant->getSession()->configure(direction, cfg, op, from, to);

In configure there is this part which sets the call id:

	if (op) {
		/* We already have an op for incoming calls */
		d->op = op;
		d->op->setUserPointer(this);
		op->enableCapabilityNegotiation (isCapabilityNegotiationEnabled());
		op->enableCnxIpTo0000IfSendOnly(
			!!linphone_config_get_default_int(
				linphone_core_get_config(core), "sip", "cnx_ip_to_0000_if_sendonly_enabled", 0
			)
		);
		linphone_call_log_set_call_id(d->log, op->getCallId().c_str()); /* Must be known at that time */
	}

But because op is always nullptr, the if branch is not used and the call id therefore is never set. We can verify this when we show the id of the CallLog object in our own (C++) code:

myLog() << call->getCallLog()->getCallId();  // Yields empty string

So retrieving the ChatRoom of a Call requires a callId of the CallLog object, but that never receives the id. How can this ever work?

mbgonicus avatar Oct 21 '21 11:10 mbgonicus