toxcore/toxav/msi.c

845 lines
23 KiB
C
Raw Normal View History

/*
* Copyright © 2016-2017 The TokTok team.
* Copyright © 2013-2015 Tox project.
2014-02-17 09:01:30 +08:00
*
* This file is part of Tox, the free peer to peer instant messenger.
*
* Tox 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 3 of the License, or
* (at your option) any later version.
*
* Tox 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 Tox. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "msi.h"
2014-04-28 01:21:26 +08:00
#include "../toxcore/logger.h"
#include "../toxcore/util.h"
#include <assert.h>
2016-09-01 07:33:20 +08:00
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MSI_MAXMSG_SIZE 256
/**
* Protocol:
*
* |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}|
*/
2014-02-17 09:01:30 +08:00
typedef enum {
IDRequest = 1,
IDError,
IDCapabilities,
} MSIHeaderID;
typedef enum {
requ_init,
2015-02-21 08:07:22 +08:00
requ_push,
requ_pop,
} MSIRequest;
#define GENERIC_HEADER(header, val_type) \
typedef struct { \
val_type value; \
bool exists; \
} MSIHeader##header
GENERIC_HEADER(Request, MSIRequest);
GENERIC_HEADER(Error, MSIError);
GENERIC_HEADER(Capabilities, uint8_t);
2014-02-17 09:01:30 +08:00
typedef struct {
MSIHeaderRequest request;
MSIHeaderError error;
MSIHeaderCapabilities capabilities;
} MSIMessage;
void msg_init(MSIMessage *dest, MSIRequest request);
int msg_parse_in(Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length);
uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length);
static int send_message(Messenger *m, uint32_t friend_number, const MSIMessage *msg);
int send_error(Messenger *m, uint32_t friend_number, MSIError error);
static int invoke_callback(MSICall *call, MSICallbackID cb);
static MSICall *get_call(MSISession *session, uint32_t friend_number);
MSICall *new_call(MSISession *session, uint32_t friend_number);
void kill_call(MSICall *call);
void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data);
void handle_init(MSICall *call, const MSIMessage *msg);
void handle_push(MSICall *call, const MSIMessage *msg);
void handle_pop(MSICall *call, const MSIMessage *msg);
void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object);
/**
* Public functions
*/
void msi_register_callback(MSISession *session, msi_action_cb *callback, MSICallbackID id)
{
2016-09-01 02:12:19 +08:00
if (!session) {
return;
2016-09-01 02:12:19 +08:00
}
2015-11-04 02:42:05 +08:00
2015-04-22 08:09:37 +08:00
pthread_mutex_lock(session->mutex);
session->callbacks[id] = callback;
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
}
MSISession *msi_new(Messenger *m)
{
if (m == nullptr) {
return nullptr;
}
MSISession *retu = (MSISession *)calloc(sizeof(MSISession), 1);
if (retu == nullptr) {
LOGGER_ERROR(m->log, "Allocation failed! Program might misbehave!");
return nullptr;
}
if (create_recursive_mutex(retu->mutex) != 0) {
LOGGER_ERROR(m->log, "Failed to init mutex! Program might misbehave");
free(retu);
return nullptr;
}
2015-04-29 07:01:25 +08:00
retu->messenger = m;
m_callback_msi_packet(m, handle_msi_packet, retu);
/* This is called when remote terminates session */
2015-04-29 07:01:25 +08:00
m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
LOGGER_DEBUG(m->log, "New msi session: %p ", retu);
return retu;
}
int msi_kill(MSISession *session, Logger *log)
{
if (session == nullptr) {
LOGGER_ERROR(log, "Tried to terminate non-existing session");
return -1;
}
m_callback_msi_packet((struct Messenger *) session->messenger, nullptr, nullptr);
2015-11-04 02:42:05 +08:00
if (pthread_mutex_trylock(session->mutex) != 0) {
2017-11-07 23:56:18 +08:00
LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
return -1;
}
if (session->calls) {
2015-02-21 08:07:22 +08:00
MSIMessage msg;
msg_init(&msg, requ_pop);
MSICall *it = get_call(session, session->calls_head);
2015-12-16 04:12:38 +08:00
while (it) {
2015-04-29 07:01:25 +08:00
send_message(session->messenger, it->friend_number, &msg);
2015-12-16 04:12:38 +08:00
MSICall *temp_it = it;
it = it->next;
kill_call(temp_it); /* This will eventually free session->calls */
}
}
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
pthread_mutex_destroy(session->mutex);
LOGGER_DEBUG(session->messenger->log, "Terminated session: %p", session);
free(session);
return 0;
}
int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
{
2016-09-01 02:12:19 +08:00
if (!session) {
return -1;
2016-09-01 02:12:19 +08:00
}
2015-11-04 02:42:05 +08:00
LOGGER_DEBUG(session->messenger->log, "Session: %p Inviting friend: %u", session, friend_number);
if (pthread_mutex_trylock(session->mutex) != 0) {
2017-11-07 23:56:18 +08:00
LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
return -1;
}
if (get_call(session, friend_number) != nullptr) {
LOGGER_ERROR(session->messenger->log, "Already in a call");
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return -1;
}
(*call) = new_call(session, friend_number);
if (*call == nullptr) {
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return -1;
2015-03-02 01:45:04 +08:00
}
(*call)->self_capabilities = capabilities;
2015-02-21 08:07:22 +08:00
MSIMessage msg;
msg_init(&msg, requ_init);
2015-02-21 08:07:22 +08:00
msg.capabilities.exists = true;
msg.capabilities.value = capabilities;
send_message((*call)->session->messenger, (*call)->friend_number, &msg);
(*call)->state = msi_CallRequesting;
LOGGER_DEBUG(session->messenger->log, "Invite sent");
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return 0;
}
int msi_hangup(MSICall *call)
{
2016-09-01 02:12:19 +08:00
if (!call || !call->session) {
return -1;
2016-09-01 02:12:19 +08:00
}
2015-11-04 02:42:05 +08:00
MSISession *session = call->session;
2015-11-04 02:42:05 +08:00
LOGGER_DEBUG(session->messenger->log, "Session: %p Hanging up call with friend: %u", call->session,
call->friend_number);
if (pthread_mutex_trylock(session->mutex) != 0) {
2017-11-07 23:56:18 +08:00
LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
return -1;
}
if (call->state == msi_CallInactive) {
LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
2015-05-13 04:16:00 +08:00
pthread_mutex_unlock(session->mutex);
return -1;
}
2015-02-21 08:07:22 +08:00
MSIMessage msg;
msg_init(&msg, requ_pop);
send_message(session->messenger, call->friend_number, &msg);
kill_call(call);
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return 0;
}
int msi_answer(MSICall *call, uint8_t capabilities)
{
2016-09-01 02:12:19 +08:00
if (!call || !call->session) {
return -1;
2016-09-01 02:12:19 +08:00
}
MSISession *session = call->session;
LOGGER_DEBUG(session->messenger->log, "Session: %p Answering call from: %u", call->session, call->friend_number);
if (pthread_mutex_trylock(session->mutex) != 0) {
2017-11-07 23:56:18 +08:00
LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
return -1;
}
if (call->state != msi_CallRequested) {
2017-11-07 23:56:18 +08:00
/* Though sending in invalid state will not cause anything weird
2015-02-21 08:07:22 +08:00
* Its better to not do it like a maniac */
LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return -1;
}
call->self_capabilities = capabilities;
2015-02-21 08:07:22 +08:00
MSIMessage msg;
msg_init(&msg, requ_push);
2015-02-21 08:07:22 +08:00
msg.capabilities.exists = true;
msg.capabilities.value = capabilities;
send_message(session->messenger, call->friend_number, &msg);
2015-02-21 08:07:22 +08:00
call->state = msi_CallActive;
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return 0;
}
int msi_change_capabilities(MSICall *call, uint8_t capabilities)
{
2016-09-01 02:12:19 +08:00
if (!call || !call->session) {
return -1;
2016-09-01 02:12:19 +08:00
}
2015-11-04 02:42:05 +08:00
MSISession *session = call->session;
LOGGER_DEBUG(session->messenger->log, "Session: %p Trying to change capabilities to friend %u", call->session,
call->friend_number);
if (pthread_mutex_trylock(session->mutex) != 0) {
2017-11-07 23:56:18 +08:00
LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
return -1;
}
if (call->state != msi_CallActive) {
LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return -1;
}
call->self_capabilities = capabilities;
2015-02-21 08:07:22 +08:00
MSIMessage msg;
msg_init(&msg, requ_push);
2015-02-21 08:07:22 +08:00
msg.capabilities.exists = true;
msg.capabilities.value = capabilities;
send_message(call->session->messenger, call->friend_number, &msg);
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return 0;
}
/**
* Private functions
*/
void msg_init(MSIMessage *dest, MSIRequest request)
2015-02-21 08:07:22 +08:00
{
memset(dest, 0, sizeof(*dest));
dest->request.exists = true;
dest->request.value = request;
}
int msg_parse_in(Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length)
2014-02-17 09:01:30 +08:00
{
/* Parse raw data received from socket into MSIMessage struct */
#define CHECK_SIZE(bytes, constraint, size) \
if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR(log, "Read over length!"); return -1; } \
if (bytes[1] != size) { LOGGER_ERROR(log, "Invalid data size!"); return -1; }
#define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \
if (bytes[2] > enum_high) { LOGGER_ERROR(log, "Failed enum high limit!"); return -1; }
#define SET_UINT8(type, bytes, header) do { \
header.value = (type)bytes[2]; \
header.exists = true; \
bytes += 3; \
} while(0)
#define SET_UINT16(bytes, header) do { \
memcpy(&header.value, bytes + 2, 2);\
header.exists = true; \
bytes += 4; \
} while(0)
assert(dest);
if (length == 0 || data[length - 1]) { /* End byte must have value 0 */
LOGGER_ERROR(log, "Invalid end byte");
2014-02-07 07:10:55 +08:00
return -1;
}
2015-02-21 08:07:22 +08:00
memset(dest, 0, sizeof(*dest));
const uint8_t *it = data;
int size_constraint = length;
while (*it) {/* until end byte is hit */
switch (*it) {
case IDRequest:
CHECK_SIZE(it, size_constraint, 1);
2015-02-21 08:07:22 +08:00
CHECK_ENUM_HIGH(it, requ_pop);
SET_UINT8(MSIRequest, it, dest->request);
2014-02-17 09:01:30 +08:00
break;
case IDError:
CHECK_SIZE(it, size_constraint, 1);
2015-03-02 01:45:04 +08:00
CHECK_ENUM_HIGH(it, msi_EUndisclosed);
SET_UINT8(MSIError, it, dest->error);
2014-02-17 09:01:30 +08:00
break;
case IDCapabilities:
CHECK_SIZE(it, size_constraint, 1);
SET_UINT8(uint8_t, it, dest->capabilities);
break;
default:
LOGGER_ERROR(log, "Invalid id byte");
return -1;
2014-04-28 01:21:26 +08:00
}
}
2015-02-21 08:07:22 +08:00
if (dest->request.exists == false) {
LOGGER_ERROR(log, "Invalid request field!");
2015-02-21 08:07:22 +08:00
return -1;
}
return 0;
#undef CHECK_SIZE
#undef CHECK_ENUM_HIGH
#undef SET_UINT8
#undef SET_UINT16
}
uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length)
{
/* Parse a single header for sending */
assert(dest);
assert(value);
assert(value_len);
*dest = id;
dest ++;
*dest = value_len;
dest ++;
memcpy(dest, value, value_len);
2014-06-15 22:36:57 +08:00
*length += (2 + value_len);
2014-06-15 22:36:57 +08:00
return dest + value_len; /* Set to next position ready to be written */
}
int send_message(Messenger *m, uint32_t friend_number, const MSIMessage *msg)
{
/* Parse and send message */
assert(m);
uint8_t parsed [MSI_MAXMSG_SIZE];
uint8_t *it = parsed;
uint16_t size = 0;
if (msg->request.exists) {
uint8_t cast = msg->request.value;
it = msg_parse_header_out(IDRequest, it, &cast,
sizeof(cast), &size);
2015-02-21 08:07:22 +08:00
} else {
LOGGER_DEBUG(m->log, "Must have request field");
2015-02-21 08:07:22 +08:00
return -1;
}
if (msg->error.exists) {
2015-02-21 08:07:22 +08:00
uint8_t cast = msg->error.value;
it = msg_parse_header_out(IDError, it, &cast,
2015-02-21 08:07:22 +08:00
sizeof(cast), &size);
}
if (msg->capabilities.exists) {
it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value,
sizeof(msg->capabilities.value), &size);
2014-07-27 01:29:49 +08:00
}
if (it == parsed) {
LOGGER_WARNING(m->log, "Parsing message failed; empty message");
2014-04-28 01:21:26 +08:00
return -1;
}
2015-02-21 08:07:22 +08:00
*it = 0;
size ++;
if (m_msi_packet(m, friend_number, parsed, size)) {
LOGGER_DEBUG(m->log, "Sent message");
2014-04-28 01:21:26 +08:00
return 0;
}
2014-04-28 01:21:26 +08:00
return -1;
}
int send_error(Messenger *m, uint32_t friend_number, MSIError error)
2014-11-18 07:46:46 +08:00
{
/* Send error message */
assert(m);
LOGGER_DEBUG(m->log, "Sending error: %d to friend: %d", error, friend_number);
2015-02-21 08:07:22 +08:00
MSIMessage msg;
msg_init(&msg, requ_pop);
2015-02-21 08:07:22 +08:00
msg.error.exists = true;
msg.error.value = error;
send_message(m, friend_number, &msg);
2014-11-18 07:46:46 +08:00
return 0;
}
int invoke_callback(MSICall *call, MSICallbackID cb)
{
assert(call);
if (call->session->callbacks[cb]) {
LOGGER_DEBUG(call->session->messenger->log, "Invoking callback function: %d", cb);
if (call->session->callbacks[cb](call->session->av, call) != 0) {
LOGGER_WARNING(call->session->messenger->log,
"Callback state handling failed, sending error");
2015-02-21 08:07:22 +08:00
goto FAILURE;
}
2015-02-21 08:07:22 +08:00
return 0;
}
2015-02-21 08:07:22 +08:00
FAILURE:
/* If no callback present or error happened while handling,
2015-03-02 01:45:04 +08:00
* an error message will be sent to friend
2015-02-21 08:07:22 +08:00
*/
2016-09-01 02:12:19 +08:00
if (call->error == msi_ENone) {
2015-03-02 01:45:04 +08:00
call->error = msi_EHandle;
2016-09-01 02:12:19 +08:00
}
2015-02-21 08:07:22 +08:00
return -1;
}
static MSICall *get_call(MSISession *session, uint32_t friend_number)
2014-02-17 09:01:30 +08:00
{
assert(session);
if (session->calls == nullptr || session->calls_tail < friend_number) {
return nullptr;
2016-09-01 02:12:19 +08:00
}
2015-04-29 07:01:25 +08:00
return session->calls[friend_number];
}
MSICall *new_call(MSISession *session, uint32_t friend_number)
2014-02-17 09:01:30 +08:00
{
assert(session);
MSICall *rc = (MSICall *)calloc(sizeof(MSICall), 1);
if (rc == nullptr) {
return nullptr;
2016-09-01 02:12:19 +08:00
}
rc->session = session;
2015-04-29 07:01:25 +08:00
rc->friend_number = friend_number;
if (session->calls == nullptr) { /* Creating */
session->calls = (MSICall **)calloc(sizeof(MSICall *), friend_number + 1);
if (session->calls == nullptr) {
free(rc);
return nullptr;
}
2015-04-29 07:01:25 +08:00
session->calls_tail = session->calls_head = friend_number;
} else if (session->calls_tail < friend_number) { /* Appending */
MSICall **tmp = (MSICall **)realloc(session->calls, sizeof(MSICall *) * (friend_number + 1));
if (tmp == nullptr) {
free(rc);
return nullptr;
}
session->calls = tmp;
/* Set fields in between to null */
uint32_t i = session->calls_tail + 1;
2016-09-01 02:12:19 +08:00
for (; i < friend_number; i ++) {
session->calls[i] = nullptr;
2016-09-01 02:12:19 +08:00
}
rc->prev = session->calls[session->calls_tail];
session->calls[session->calls_tail]->next = rc;
2015-04-29 07:01:25 +08:00
session->calls_tail = friend_number;
} else if (session->calls_head > friend_number) { /* Inserting at front */
rc->next = session->calls[session->calls_head];
session->calls[session->calls_head]->prev = rc;
2015-04-29 07:01:25 +08:00
session->calls_head = friend_number;
2014-04-28 01:21:26 +08:00
}
2015-04-29 07:01:25 +08:00
session->calls[friend_number] = rc;
return rc;
}
void kill_call(MSICall *call)
{
2015-03-02 01:45:04 +08:00
/* Assume that session mutex is locked */
if (call == nullptr) {
return;
2016-09-01 02:12:19 +08:00
}
MSISession *session = call->session;
LOGGER_DEBUG(session->messenger->log, "Killing call: %p", call);
MSICall *prev = call->prev;
MSICall *next = call->next;
2016-09-01 02:12:19 +08:00
if (prev) {
prev->next = next;
2016-09-01 02:12:19 +08:00
} else if (next) {
2015-04-29 07:01:25 +08:00
session->calls_head = next->friend_number;
2016-09-01 02:12:19 +08:00
} else {
goto CLEAR_CONTAINER;
}
2016-09-01 02:12:19 +08:00
if (next) {
next->prev = prev;
2016-09-01 02:12:19 +08:00
} else if (prev) {
2015-04-29 07:01:25 +08:00
session->calls_tail = prev->friend_number;
2016-09-01 02:12:19 +08:00
} else {
goto CLEAR_CONTAINER;
}
session->calls[call->friend_number] = nullptr;
free(call);
return;
2015-03-02 01:45:04 +08:00
CLEAR_CONTAINER:
session->calls_head = session->calls_tail = 0;
free(session->calls);
free(call);
session->calls = nullptr;
}
void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data)
2014-11-18 07:46:46 +08:00
{
(void)m;
MSISession *session = (MSISession *)data;
switch (status) {
case 0: { /* Friend is now offline */
LOGGER_DEBUG(m->log, "Friend %d is now offline", friend_number);
2015-04-22 08:09:37 +08:00
pthread_mutex_lock(session->mutex);
MSICall *call = get_call(session, friend_number);
if (call == nullptr) {
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return;
2015-03-02 01:45:04 +08:00
}
invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
kill_call(call);
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
2014-11-18 07:46:46 +08:00
}
break;
2014-11-18 07:46:46 +08:00
default:
break;
}
}
void handle_init(MSICall *call, const MSIMessage *msg)
{
assert(call);
LOGGER_DEBUG(call->session->messenger->log,
"Session: %p Handling 'init' friend: %d", call->session, call->friend_number);
2015-11-04 02:42:05 +08:00
if (!msg->capabilities.exists) {
LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid capabilities on 'init'");
2015-03-02 01:45:04 +08:00
call->error = msi_EInvalidMessage;
2015-02-21 08:07:22 +08:00
goto FAILURE;
}
2015-11-04 02:42:05 +08:00
switch (call->state) {
2015-02-21 08:07:22 +08:00
case msi_CallInactive: {
/* Call requested */
call->peer_capabilities = msg->capabilities.value;
call->state = msi_CallRequested;
if (invoke_callback(call, msi_OnInvite) == -1) {
2015-02-21 08:07:22 +08:00
goto FAILURE;
2016-09-01 02:12:19 +08:00
}
}
break;
2015-11-04 02:42:05 +08:00
2015-02-21 08:07:22 +08:00
case msi_CallActive: {
/* If peer sent init while the call is already
* active it's probable that he is trying to
* re-call us while the call is not terminated
* on our side. We can assume that in this case
* we can automatically answer the re-call.
*/
2015-11-04 02:42:05 +08:00
LOGGER_INFO(call->session->messenger->log, "Friend is recalling us");
2015-11-04 02:42:05 +08:00
MSIMessage out_msg;
msg_init(&out_msg, requ_push);
out_msg.capabilities.exists = true;
out_msg.capabilities.value = call->self_capabilities;
send_message(call->session->messenger, call->friend_number, &out_msg);
/* If peer changed capabilities during re-call they will
* be handled accordingly during the next step
*/
}
break;
2015-11-04 02:42:05 +08:00
case msi_CallRequested: // fall-through
case msi_CallRequesting: {
LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid state on 'init'");
call->error = msi_EInvalidState;
goto FAILURE;
}
}
2015-11-04 02:42:05 +08:00
return;
FAILURE:
send_error(call->session->messenger, call->friend_number, call->error);
kill_call(call);
}
void handle_push(MSICall *call, const MSIMessage *msg)
{
assert(call);
LOGGER_DEBUG(call->session->messenger->log, "Session: %p Handling 'push' friend: %d", call->session,
call->friend_number);
if (!msg->capabilities.exists) {
LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid capabilities on 'push'");
call->error = msi_EInvalidMessage;
goto FAILURE;
}
switch (call->state) {
case msi_CallActive: {
2015-02-21 08:07:22 +08:00
/* Only act if capabilities changed */
if (call->peer_capabilities != msg->capabilities.value) {
LOGGER_INFO(call->session->messenger->log, "Friend is changing capabilities to: %u", msg->capabilities.value);
2015-02-21 08:07:22 +08:00
call->peer_capabilities = msg->capabilities.value;
if (invoke_callback(call, msi_OnCapabilities) == -1) {
2015-02-21 08:07:22 +08:00
goto FAILURE;
2016-09-01 02:12:19 +08:00
}
2015-02-21 08:07:22 +08:00
}
}
break;
2015-02-21 08:07:22 +08:00
case msi_CallRequesting: {
LOGGER_INFO(call->session->messenger->log, "Friend answered our call");
2015-02-21 08:07:22 +08:00
/* Call started */
call->peer_capabilities = msg->capabilities.value;
2015-02-21 08:07:22 +08:00
call->state = msi_CallActive;
if (invoke_callback(call, msi_OnStart) == -1) {
2015-02-21 08:07:22 +08:00
goto FAILURE;
2016-09-01 02:12:19 +08:00
}
}
break;
/* Pushes during initialization state are ignored */
case msi_CallInactive: // fall-through
2015-02-21 08:07:22 +08:00
case msi_CallRequested: {
LOGGER_WARNING(call->session->messenger->log, "Ignoring invalid push");
}
break;
}
2015-02-21 08:07:22 +08:00
return;
2015-02-21 08:07:22 +08:00
FAILURE:
2015-04-29 07:01:25 +08:00
send_error(call->session->messenger, call->friend_number, call->error);
kill_call(call);
}
void handle_pop(MSICall *call, const MSIMessage *msg)
2014-02-17 09:01:30 +08:00
{
assert(call);
LOGGER_DEBUG(call->session->messenger->log, "Session: %p Handling 'pop', friend id: %d", call->session,
call->friend_number);
2015-02-21 08:07:22 +08:00
/* callback errors are ignored */
2015-02-21 08:07:22 +08:00
if (msg->error.exists) {
LOGGER_WARNING(call->session->messenger->log, "Friend detected an error: %d", msg->error.value);
2015-02-21 08:07:22 +08:00
call->error = msg->error.value;
invoke_callback(call, msi_OnError);
2016-09-01 02:12:19 +08:00
} else {
switch (call->state) {
2015-11-04 02:42:05 +08:00
case msi_CallInactive: {
LOGGER_ERROR(call->session->messenger->log, "Handling what should be impossible case");
2015-11-04 02:42:05 +08:00
abort();
}
2015-11-04 02:42:05 +08:00
case msi_CallActive: {
/* Hangup */
LOGGER_INFO(call->session->messenger->log, "Friend hung up on us");
invoke_callback(call, msi_OnEnd);
2015-11-04 02:42:05 +08:00
}
break;
2015-11-04 02:42:05 +08:00
case msi_CallRequesting: {
/* Reject */
LOGGER_INFO(call->session->messenger->log, "Friend rejected our call");
invoke_callback(call, msi_OnEnd);
2015-11-04 02:42:05 +08:00
}
break;
2015-11-04 02:42:05 +08:00
case msi_CallRequested: {
/* Cancel */
LOGGER_INFO(call->session->messenger->log, "Friend canceled call invite");
invoke_callback(call, msi_OnEnd);
2015-11-04 02:42:05 +08:00
}
break;
}
2016-09-01 02:12:19 +08:00
}
kill_call(call);
}
void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
2014-02-01 19:52:48 +08:00
{
LOGGER_DEBUG(m->log, "Got msi message");
MSISession *session = (MSISession *)object;
MSIMessage msg;
if (msg_parse_in(m->log, &msg, data, length) == -1) {
LOGGER_WARNING(m->log, "Error parsing message");
2015-04-29 07:01:25 +08:00
send_error(m, friend_number, msi_EInvalidMessage);
2014-04-28 01:21:26 +08:00
return;
}
LOGGER_DEBUG(m->log, "Successfully parsed message");
2015-04-22 08:09:37 +08:00
pthread_mutex_lock(session->mutex);
2015-04-29 07:01:25 +08:00
MSICall *call = get_call(session, friend_number);
if (call == nullptr) {
if (msg.request.value != requ_init) {
2015-04-29 07:01:25 +08:00
send_error(m, friend_number, msi_EStrayMessage);
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return;
}
2015-04-29 07:01:25 +08:00
call = new_call(session, friend_number);
if (call == nullptr) {
2015-04-29 07:01:25 +08:00
send_error(m, friend_number, msi_ESystem);
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
return;
}
}
switch (msg.request.value) {
case requ_init:
handle_init(call, &msg);
break;
2015-11-04 02:42:05 +08:00
case requ_push:
handle_push(call, &msg);
break;
2015-11-04 02:42:05 +08:00
case requ_pop:
handle_pop(call, &msg); /* always kills the call */
break;
}
2015-04-22 08:09:37 +08:00
pthread_mutex_unlock(session->mutex);
2014-04-17 00:08:44 +08:00
}