2023-07-25 11:53:09 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
* Copyright © 2016-2018 The TokTok team.
|
|
|
|
* Copyright © 2013-2015 Tox project.
|
|
|
|
*/
|
|
|
|
#include "msi.h"
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
#include "toxav_hacks.h"
|
|
|
|
|
2023-07-25 11:53:09 +02:00
|
|
|
#include "../toxcore/ccompat.h"
|
|
|
|
#include "../toxcore/logger.h"
|
2024-11-09 13:44:30 +01:00
|
|
|
#include "../toxcore/net_crypto.h"
|
|
|
|
#include "../toxcore/tox.h"
|
|
|
|
#include "../toxcore/tox_private.h"
|
2023-07-25 11:53:09 +02:00
|
|
|
#include "../toxcore/util.h"
|
|
|
|
|
|
|
|
#define MSI_MAXMSG_SIZE 256
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Protocol:
|
|
|
|
*
|
|
|
|
* `|id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}|`
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef enum MSIHeaderID {
|
|
|
|
ID_REQUEST = 1,
|
|
|
|
ID_ERROR,
|
|
|
|
ID_CAPABILITIES,
|
|
|
|
} MSIHeaderID;
|
|
|
|
|
|
|
|
typedef enum MSIRequest {
|
|
|
|
REQU_INIT,
|
|
|
|
REQU_PUSH,
|
|
|
|
REQU_POP,
|
|
|
|
} MSIRequest;
|
|
|
|
|
|
|
|
typedef struct MSIHeaderRequest {
|
|
|
|
MSIRequest value;
|
|
|
|
bool exists;
|
|
|
|
} MSIHeaderRequest;
|
|
|
|
|
|
|
|
typedef struct MSIHeaderError {
|
|
|
|
MSIError value;
|
|
|
|
bool exists;
|
|
|
|
} MSIHeaderError;
|
|
|
|
|
|
|
|
typedef struct MSIHeaderCapabilities {
|
|
|
|
uint8_t value;
|
|
|
|
bool exists;
|
|
|
|
} MSIHeaderCapabilities;
|
|
|
|
|
|
|
|
typedef struct MSIMessage {
|
|
|
|
MSIHeaderRequest request;
|
|
|
|
MSIHeaderError error;
|
|
|
|
MSIHeaderCapabilities capabilities;
|
|
|
|
} MSIMessage;
|
|
|
|
|
|
|
|
static void msg_init(MSIMessage *dest, MSIRequest request);
|
2024-11-09 13:44:30 +01:00
|
|
|
static void kill_call(const Logger *log, MSICall *call);
|
2023-07-25 11:53:09 +02:00
|
|
|
static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length);
|
2024-01-09 16:39:05 +01:00
|
|
|
static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len,
|
2023-07-25 11:53:09 +02:00
|
|
|
uint16_t *length);
|
2024-11-09 13:44:30 +01:00
|
|
|
static int send_message(const Logger *log, Tox *tox, uint32_t friend_number, const MSIMessage *msg);
|
|
|
|
static int send_error(const Logger *log, Tox *tox, uint32_t friend_number, MSIError error);
|
2023-07-25 11:53:09 +02:00
|
|
|
static MSICall *get_call(MSISession *session, uint32_t friend_number);
|
|
|
|
static MSICall *new_call(MSISession *session, uint32_t friend_number);
|
2024-11-09 13:44:30 +01:00
|
|
|
static bool invoke_callback(const Logger *log, MSICall *call, MSICallbackID cb);
|
|
|
|
static void handle_init(const Logger *log, MSICall *call, const MSIMessage *msg);
|
|
|
|
static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg);
|
|
|
|
static void handle_pop(const Logger *log, MSICall *call, const MSIMessage *msg);
|
|
|
|
static void handle_msi_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length,
|
|
|
|
void *user_data);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Public functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
void msi_callback_invite(MSISession *session, msi_action_cb *callback)
|
|
|
|
{
|
|
|
|
session->invite_callback = callback;
|
|
|
|
}
|
|
|
|
void msi_callback_start(MSISession *session, msi_action_cb *callback)
|
|
|
|
{
|
|
|
|
session->start_callback = callback;
|
|
|
|
}
|
|
|
|
void msi_callback_end(MSISession *session, msi_action_cb *callback)
|
|
|
|
{
|
|
|
|
session->end_callback = callback;
|
|
|
|
}
|
|
|
|
void msi_callback_error(MSISession *session, msi_action_cb *callback)
|
|
|
|
{
|
|
|
|
session->error_callback = callback;
|
|
|
|
}
|
|
|
|
void msi_callback_peertimeout(MSISession *session, msi_action_cb *callback)
|
|
|
|
{
|
|
|
|
session->peertimeout_callback = callback;
|
|
|
|
}
|
|
|
|
void msi_callback_capabilities(MSISession *session, msi_action_cb *callback)
|
|
|
|
{
|
|
|
|
session->capabilities_callback = callback;
|
|
|
|
}
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
MSISession *msi_new(const Logger *log, Tox *tox)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
2024-11-09 13:44:30 +01:00
|
|
|
if (tox == nullptr) {
|
2023-07-25 11:53:09 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
MSISession *retu = (MSISession *)calloc(1, sizeof(MSISession));
|
|
|
|
|
|
|
|
if (retu == nullptr) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Allocation failed! Program might misbehave!");
|
2023-07-25 11:53:09 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (create_recursive_mutex(retu->mutex) != 0) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Failed to init mutex! Program might misbehave");
|
2023-07-25 11:53:09 +02:00
|
|
|
free(retu);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
retu->tox = tox;
|
2023-07-25 11:53:09 +02:00
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
// register callback
|
|
|
|
tox_callback_friend_lossless_packet_per_pktid(tox, handle_msi_packet, PACKET_ID_MSI);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "New msi session: %p ", (void *)retu);
|
2023-07-25 11:53:09 +02:00
|
|
|
return retu;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
int msi_kill(const Logger *log, Tox *tox, MSISession *session)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
if (session == nullptr) {
|
|
|
|
LOGGER_ERROR(log, "Tried to terminate non-existing session");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
// UN-register callback
|
|
|
|
tox_callback_friend_lossless_packet_per_pktid(tox, nullptr, PACKET_ID_MSI);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
if (pthread_mutex_trylock(session->mutex) != 0) {
|
|
|
|
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (session->calls != nullptr) {
|
|
|
|
MSIMessage msg;
|
|
|
|
msg_init(&msg, REQU_POP);
|
|
|
|
|
|
|
|
MSICall *it = get_call(session, session->calls_head);
|
|
|
|
|
|
|
|
while (it != nullptr) {
|
2024-11-09 13:44:30 +01:00
|
|
|
send_message(log, session->tox, it->friend_number, &msg);
|
2023-07-25 11:53:09 +02:00
|
|
|
MSICall *temp_it = it;
|
|
|
|
it = it->next;
|
2024-11-09 13:44:30 +01:00
|
|
|
kill_call(log, temp_it); /* This will eventually free session->calls */
|
2023-07-25 11:53:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
pthread_mutex_destroy(session->mutex);
|
|
|
|
|
|
|
|
LOGGER_DEBUG(log, "Terminated session: %p", (void *)session);
|
|
|
|
free(session);
|
|
|
|
return 0;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* return true if friend is offline and the call was canceled.
|
|
|
|
*/
|
|
|
|
bool check_peer_offline_status(const Logger *log, const Tox *tox, MSISession *session, uint32_t friend_number)
|
|
|
|
{
|
|
|
|
if (tox == nullptr || session == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Tox_Err_Friend_Query f_con_query_error;
|
|
|
|
const Tox_Connection f_con_status = tox_friend_get_connection_status(tox, friend_number, &f_con_query_error);
|
|
|
|
|
|
|
|
if (f_con_status == TOX_CONNECTION_NONE) {
|
|
|
|
/* Friend is now offline */
|
|
|
|
LOGGER_DEBUG(log, "Friend %d is now offline", friend_number);
|
|
|
|
|
|
|
|
pthread_mutex_lock(session->mutex);
|
|
|
|
MSICall *call = get_call(session, friend_number);
|
|
|
|
|
|
|
|
if (call == nullptr) {
|
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
invoke_callback(log, call, MSI_ON_PEERTIMEOUT); /* Failure is ignored */
|
|
|
|
kill_call(log, call);
|
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int msi_invite(const Logger *log, MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "msi_invite:session:%p", (void *)session);
|
|
|
|
|
2023-07-25 11:53:09 +02:00
|
|
|
if (session == nullptr) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Session: %p Inviting friend: %u", (void *)session, friend_number);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
if (pthread_mutex_trylock(session->mutex) != 0) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
2023-07-25 11:53:09 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (get_call(session, friend_number) != nullptr) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Already in a call");
|
2023-07-25 11:53:09 +02:00
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
MSICall *temp = new_call(session, friend_number);
|
|
|
|
|
|
|
|
if (temp == nullptr) {
|
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp->self_capabilities = capabilities;
|
|
|
|
|
|
|
|
MSIMessage msg;
|
|
|
|
msg_init(&msg, REQU_INIT);
|
|
|
|
|
|
|
|
msg.capabilities.exists = true;
|
|
|
|
msg.capabilities.value = capabilities;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
send_message(log, temp->session->tox, temp->friend_number, &msg);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
temp->state = MSI_CALL_REQUESTING;
|
|
|
|
|
|
|
|
*call = temp;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Invite sent");
|
2023-07-25 11:53:09 +02:00
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
int msi_hangup(const Logger *log, MSICall *call)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
if (call == nullptr || call->session == nullptr) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
MSISession *session = call->session;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Session: %p Hanging up call with friend: %u", (void *)call->session,
|
2023-07-25 11:53:09 +02:00
|
|
|
call->friend_number);
|
|
|
|
|
|
|
|
if (pthread_mutex_trylock(session->mutex) != 0) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
2023-07-25 11:53:09 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (call->state == MSI_CALL_INACTIVE) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Call is in invalid state!");
|
2023-07-25 11:53:09 +02:00
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
MSIMessage msg;
|
|
|
|
msg_init(&msg, REQU_POP);
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
send_message(log, session->tox, call->friend_number, &msg);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
kill_call(log, call);
|
2023-07-25 11:53:09 +02:00
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
int msi_answer(const Logger *log, MSICall *call, uint8_t capabilities)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
if (call == nullptr || call->session == nullptr) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
MSISession *session = call->session;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Session: %p Answering call from: %u", (void *)call->session,
|
2023-07-25 11:53:09 +02:00
|
|
|
call->friend_number);
|
|
|
|
|
|
|
|
if (pthread_mutex_trylock(session->mutex) != 0) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
2023-07-25 11:53:09 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (call->state != MSI_CALL_REQUESTED) {
|
|
|
|
/* Though sending in invalid state will not cause anything weird
|
|
|
|
* Its better to not do it like a maniac */
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Call is in invalid state!");
|
2023-07-25 11:53:09 +02:00
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
call->self_capabilities = capabilities;
|
|
|
|
|
|
|
|
MSIMessage msg;
|
|
|
|
msg_init(&msg, REQU_PUSH);
|
|
|
|
|
|
|
|
msg.capabilities.exists = true;
|
|
|
|
msg.capabilities.value = capabilities;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
send_message(log, session->tox, call->friend_number, &msg);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
call->state = MSI_CALL_ACTIVE;
|
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
int msi_change_capabilities(const Logger *log, MSICall *call, uint8_t capabilities)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
if (call == nullptr || call->session == nullptr) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
MSISession *session = call->session;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Session: %p Trying to change capabilities to friend %u", (void *)call->session,
|
2023-07-25 11:53:09 +02:00
|
|
|
call->friend_number);
|
|
|
|
|
|
|
|
if (pthread_mutex_trylock(session->mutex) != 0) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
2023-07-25 11:53:09 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (call->state != MSI_CALL_ACTIVE) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_ERROR(log, "Call is in invalid state!");
|
2023-07-25 11:53:09 +02:00
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
call->self_capabilities = capabilities;
|
|
|
|
|
|
|
|
MSIMessage msg;
|
|
|
|
msg_init(&msg, REQU_PUSH);
|
|
|
|
|
|
|
|
msg.capabilities.exists = true;
|
|
|
|
msg.capabilities.value = capabilities;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
send_message(log, call->session->tox, call->friend_number, &msg);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private functions
|
|
|
|
*/
|
|
|
|
static void msg_init(MSIMessage *dest, MSIRequest request)
|
|
|
|
{
|
|
|
|
memset(dest, 0, sizeof(*dest));
|
|
|
|
dest->request.exists = true;
|
|
|
|
dest->request.value = request;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool check_size(const Logger *log, const uint8_t *bytes, int *constraint, uint8_t size)
|
|
|
|
{
|
|
|
|
*constraint -= 2 + size;
|
|
|
|
|
|
|
|
if (*constraint < 1) {
|
|
|
|
LOGGER_ERROR(log, "Read over length!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bytes[1] != size) {
|
|
|
|
LOGGER_ERROR(log, "Invalid data size!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Assumes size == 1 */
|
|
|
|
static bool check_enum_high(const Logger *log, const uint8_t *bytes, uint8_t enum_high)
|
|
|
|
{
|
|
|
|
if (bytes[2] > enum_high) {
|
|
|
|
LOGGER_ERROR(log, "Failed enum high limit!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
static const uint8_t *msg_parse_one(const Logger *log, MSIMessage *dest, const uint8_t *it, int *size_constraint)
|
|
|
|
{
|
|
|
|
switch (*it) {
|
|
|
|
case ID_REQUEST: {
|
|
|
|
if (!check_size(log, it, size_constraint, 1) ||
|
|
|
|
!check_enum_high(log, it, REQU_POP)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest->request.value = (MSIRequest)it[2];
|
|
|
|
dest->request.exists = true;
|
|
|
|
return it + 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ID_ERROR: {
|
|
|
|
if (!check_size(log, it, size_constraint, 1) ||
|
|
|
|
!check_enum_high(log, it, MSI_E_UNDISCLOSED)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest->error.value = (MSIError)it[2];
|
|
|
|
dest->error.exists = true;
|
|
|
|
return it + 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ID_CAPABILITIES: {
|
|
|
|
if (!check_size(log, it, size_constraint, 1)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest->capabilities.value = it[2];
|
|
|
|
dest->capabilities.exists = true;
|
|
|
|
return it + 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
|
|
|
LOGGER_ERROR(log, "Invalid id byte: %d", *it);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-25 11:53:09 +02:00
|
|
|
static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length)
|
|
|
|
{
|
|
|
|
/* Parse raw data received from socket into MSIMessage struct */
|
|
|
|
assert(dest != nullptr);
|
|
|
|
|
|
|
|
if (length == 0 || data[length - 1] != 0) { /* End byte must have value 0 */
|
|
|
|
LOGGER_ERROR(log, "Invalid end byte");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(dest, 0, sizeof(*dest));
|
|
|
|
|
|
|
|
const uint8_t *it = data;
|
|
|
|
int size_constraint = length;
|
|
|
|
|
|
|
|
while (*it != 0) {/* until end byte is hit */
|
2024-11-09 13:44:30 +01:00
|
|
|
it = msg_parse_one(log, dest, it, &size_constraint);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
if (it == nullptr) {
|
|
|
|
return -1;
|
2023-07-25 11:53:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dest->request.exists) {
|
|
|
|
LOGGER_ERROR(log, "Invalid request field!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
2024-01-09 16:39:05 +01:00
|
|
|
static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len,
|
2023-07-25 11:53:09 +02:00
|
|
|
uint16_t *length)
|
|
|
|
{
|
|
|
|
/* Parse a single header for sending */
|
|
|
|
assert(dest != nullptr);
|
|
|
|
assert(value != nullptr);
|
|
|
|
assert(value_len != 0);
|
|
|
|
|
|
|
|
*dest = id;
|
|
|
|
++dest;
|
|
|
|
*dest = value_len;
|
|
|
|
++dest;
|
|
|
|
|
|
|
|
memcpy(dest, value, value_len);
|
|
|
|
|
|
|
|
*length += 2 + value_len;
|
|
|
|
|
|
|
|
return dest + value_len; /* Set to next position ready to be written */
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
/* Send an msi packet.
|
|
|
|
*
|
|
|
|
* return 1 on success
|
|
|
|
* return 0 on failure
|
|
|
|
*/
|
|
|
|
static int m_msi_packet(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
2024-11-09 13:44:30 +01:00
|
|
|
// TODO(Zoff): make this better later! -------------------
|
|
|
|
/* we need to prepend 1 byte (packet id) to data
|
|
|
|
* do this without malloc, memcpy and free in the future
|
|
|
|
*/
|
|
|
|
const size_t length_new = (size_t)length + 1;
|
|
|
|
uint8_t *data_new = (uint8_t *)malloc(length_new);
|
|
|
|
|
|
|
|
if (data_new == nullptr) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_new[0] = PACKET_ID_MSI;
|
|
|
|
|
|
|
|
if (length != 0) {
|
|
|
|
memcpy(data_new + 1, data, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
Tox_Err_Friend_Custom_Packet error;
|
|
|
|
tox_friend_send_lossless_packet(tox, friendnumber, data_new, length_new, &error);
|
|
|
|
|
|
|
|
free(data_new);
|
|
|
|
|
|
|
|
if (error == TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int send_message(const Logger *log, Tox *tox, uint32_t friend_number, const MSIMessage *msg)
|
|
|
|
{
|
|
|
|
assert(tox != nullptr);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
/* Parse and send message */
|
Squashed 'external/toxcore/c-toxcore/' changes from e2c01e457b..b03b571272
b03b571272 fix: flaky tcp test This only fixes the symptoms, not the real problem. Sometimes or consistently on some platforms a socket might need a moment before it can be written to.
32e67ab4c2 cleanup: use typedef for private message ID's in callback
7b1db6adc1 feat: add message IDs to private group messages
99e0bcc27d refactor: Observers/ignored peers can now send and receive custom packets
b3c3c49d26 fix: Disable IPv6 in Windows cross-compilation tests
e742deddff feat: Check hashes of Windows dependencies when cross-compiling
dfb9a0b02b fix: Test the current Windows Dockerfile, not an old Dockerhub image
14de93ccec chore: Use WineHQ's Wine as Debian Bookworm's crashes
ed37616249 docs: Update the Windows cross-compilation section
9bb79c174f cleanup: Remove a couple of unnecessary misc_tools dependencies
19475adb70 chore: Statically link OpenMP into the cracker fun util on Windows
1be311e51f feat: Build the fun utils when cross-compiling to Windows
88133f8446 chore: Strip Windows binaries
3cc0ae7535 refactor: Copy over all of the required static dependencies
c4fa8f7fb1 feat: Generate .def, .exp and .lib files when building for Windows
74bbac5363 feat: Let CMake create the dll instead of doing so ourselves
246642e9ae feat: Harden Windows cross-compilation
8d431c0d11 chore: Bump Windows build dependency versions
e519f7998b fix: Remove unnecessary wsock32 dependency on Windows
ed2b60c217 chore: Use a specific non-broken slimcc version.
d7f21010a1 chore: Update github actions.
e71a68b7f2 docs: Update the list of CMake options
77e08876ff chore: Remove mod and founder from group API naming scheme
12bc042767 docs: add the experimental api build option to INSTALL.md
e1fa5cae96 refactor: Rename Queries to Query to align with other enums.
be82a3ea30 fix: Correct type for conference offline peer numbers.
0627c36716 test: Add pkgsrc build.
92578afe4b test: Add FreeBSD VM action on GitHub.
52ece0f57b test: Build toxcore on NetBSD (VM).
3fe8ee2c11 chore: Only install tox_private.h on request.
9a8dfa06ab fix: save_compatibility_test failing on big-endian systems
86f5e55578 fix: Don't serve files from websockify.
710eb674a5 fix: Correctly pass extended public keys to group moderation code.
021db7031c refactor: Use `struct`s for extended public/secret keys.
a1e999fd80 chore: Compile libsodium reference implementation with compcert.
fbe3c19cf5 cleanup: correct a few nullable annotations
623e3ee5c3 cleanup: Don't use `memcpy` to cast arbitrary `struct`s to `uint8_t[]`.
c71567dc18 fix: Pass array, not array pointer, to `memcmp`.
9b46a08144 cleanup: Never pass `void*` directly to `memcpy`.
5d7b7a7bbc refactor: Use tox rng to seed the keypair generation.
961891d568 cleanup: Small improvements found by PVS Studio.
8201019f0d chore: Disable NGC saving by default, enable through Tox_Options.
5dd9ee3f65 cleanup: Replace pointer arithmetic with explicit `&arr[i]`.
ca4606d49d refactor: Use strong typedef for NGC peer id.
442213b722 cleanup: Simplify custom packet length check in NGC.
08d3393def fix: Correct a few potential null derefs in bootstrap daemon.
b9877b32b0 fix: Add missing memunlock of local variable when it goes out of scope.
dab5fe44b9 fix: Zero out stack-allocated secret key before return.
f058103299 refactor: Make prune_gc_sanctions_list more obviously correct.
3ba7a0dec9 docs: Add static analysis tool list to README.
8d0811a0f3 docs: Run prettier-markdown on markdown files.
969e3a2bfc refactor: Fix network test not using the strong typedef
93c83fbc7c refactor: Use strong typedef instead of struct for `Socket`.
9fe18b176f fix: Fix some false positive from PVS Studio.
7c44379ccb cleanup: Check that WINXP macro exists before comparing it.
5c93231bef refactor: Make tox mutex non-recursive.
aacff73939 docs: Fix up doxyfile.
d55fc85ff5 docs: Add more documentation to crypto_core.
5bdaaaedb6 refactor: Remove `Tox *` from `tox_dispatch`.
e202341e76 refactor: Don't rely on tox_dispatch passing tox in tests.
34df938f52 chore: Use C++ mode for clang-tidy.
8b05296a78 chore: Check that both gtest and gmock exist for tests.
42010660e1 test: Add slimcc compiler compatibility test.
b473630321 chore: Add some comments to the astyle config.
b7404f24f6 cleanup: Remove implicit bool conversions.
4e2dba4d9f chore: Reformat sources with astyle.
4359e3a6bc chore: Rename C++ headers to .hh suffixes.
0c05566e58 cleanup: Further `#include` cleanups.
8d29935b7a chore: Only check the bootstrap daemon checksum on release.
f70e588bc6 cleanup: Add more `const` where possible.
511bfe39c8 cleanup: Use Bazel modules to enforce proper `#include` hygiene.
1710a0d091 refactor: Move pack/unpack `IP_Port` from DHT into network module.
a975943564 chore: Really fix coverage docker image build.
c08409390f chore: Fix post-submit coverage image.
39aadf8922 fix: Don't use `memcmp` to compare `IP_Port`s.
d94246a906 fix: partially fix a bug that prevented group part messages from sending.
eeaa039222 chore: Fix rpm build; add a CI check for it.
8328449c1a chore: Speed up docker builds a bit by reducing layer count.
d6d67d56f3 cleanup: Add `const` where possible in auto tests.
6aa9e6850d cleanup: Minor cleanup of event unpack code.
bdf460a3a9 refactor: Rename `system_{memory,...}` to `os_{memory,...}`.
203e1af81e fix: a few off by one errors in group autotests
5c093c4888 cleanup: Remove all uses of `SIZEOF_VLA`.
662c2140f3 test: Add goblint static analyser.
8f07755834 cleanup: Use `memzero(x, s)` instead of `memset(x, 0, s)`.
a7258e40cf cleanup: Use explicit 0 instead of `PACKET_ID_PADDING`.
6370d0f15d cleanup: Expand the `Tox_Options` accessor macros.
14a1a0b9bd cleanup: Remove plan9 support.
a05dccad13 test: Add a simple new/delete test for Tox.
1cdcf938b9 cleanup: Add comment after every `#endif`.
ba99d4dc4b test: Fix comment I broke in the events test PR.
e07248debb refactor: Migrate auto_tests to new events API.
bdd42b5452 refactor: Add common msgpack array packer with callback.
3c659f5288 cleanup: Rename group to conference in groupav documentation.
89957be230 cleanup: Ensure handler params are named after callback params.
c650d9d345 refactor: Pass `this` pointer as first param to s11n callbacks.
e7fb91ddb8 refactor: Allow NULL pointers for byte arrays in events.
5e2c8cabc1 cleanup: make some improvements to group moderation test
259de4867e cleanup: Remove `bin_pack_{new,free}`.
21a8ff5895 cleanup: skip a do_gc iteration before removing peers marked for deletion
16809dc36e feat: Add dht_get_nodes_response event to the events system.
git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: b03b5712720de9a9901ea12fd741f177327a7021
2024-03-07 23:12:55 +01:00
|
|
|
uint8_t parsed[MSI_MAXMSG_SIZE];
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
uint8_t *it = parsed;
|
|
|
|
uint16_t size = 0;
|
|
|
|
|
|
|
|
if (msg->request.exists) {
|
|
|
|
uint8_t cast = msg->request.value;
|
|
|
|
it = msg_parse_header_out(ID_REQUEST, it, &cast,
|
|
|
|
sizeof(cast), &size);
|
|
|
|
} else {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Must have request field");
|
2023-07-25 11:53:09 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->error.exists) {
|
|
|
|
uint8_t cast = msg->error.value;
|
|
|
|
it = msg_parse_header_out(ID_ERROR, it, &cast,
|
|
|
|
sizeof(cast), &size);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->capabilities.exists) {
|
|
|
|
it = msg_parse_header_out(ID_CAPABILITIES, it, &msg->capabilities.value,
|
|
|
|
sizeof(msg->capabilities.value), &size);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (it == parsed) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_WARNING(log, "Parsing message failed; empty message");
|
2023-07-25 11:53:09 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*it = 0;
|
|
|
|
++size;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
if (m_msi_packet(tox, friend_number, parsed, size) == 1) {
|
|
|
|
LOGGER_DEBUG(log, "Sent message");
|
2023-07-25 11:53:09 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
static int send_error(const Logger *log, Tox *tox, uint32_t friend_number, MSIError error)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
2024-11-09 13:44:30 +01:00
|
|
|
assert(tox != nullptr);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
/* Send error message */
|
|
|
|
LOGGER_DEBUG(log, "Sending error: %d to friend: %d", error, friend_number);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
MSIMessage msg;
|
|
|
|
msg_init(&msg, REQU_POP);
|
|
|
|
|
|
|
|
msg.error.exists = true;
|
|
|
|
msg.error.value = error;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
send_message(log, tox, friend_number, &msg);
|
2023-07-25 11:53:09 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
static int invoke_callback_inner(const Logger *log, MSICall *call, MSICallbackID id)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
MSISession *session = call->session;
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "invoking callback function: %d", id);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
switch (id) {
|
|
|
|
case MSI_ON_INVITE:
|
|
|
|
return session->invite_callback(session->av, call);
|
|
|
|
|
|
|
|
case MSI_ON_START:
|
|
|
|
return session->start_callback(session->av, call);
|
|
|
|
|
|
|
|
case MSI_ON_END:
|
|
|
|
return session->end_callback(session->av, call);
|
|
|
|
|
|
|
|
case MSI_ON_ERROR:
|
|
|
|
return session->error_callback(session->av, call);
|
|
|
|
|
|
|
|
case MSI_ON_PEERTIMEOUT:
|
|
|
|
return session->peertimeout_callback(session->av, call);
|
|
|
|
|
|
|
|
case MSI_ON_CAPABILITIES:
|
|
|
|
return session->capabilities_callback(session->av, call);
|
|
|
|
}
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_FATAL(log, "invalid callback id: %d", id);
|
2023-07-25 11:53:09 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
static bool invoke_callback(const Logger *log, MSICall *call, MSICallbackID cb)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
assert(call != nullptr);
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
if (invoke_callback_inner(log, call, cb) != 0) {
|
|
|
|
LOGGER_WARNING(log,
|
2023-07-25 11:53:09 +02:00
|
|
|
"Callback state handling failed, sending error");
|
|
|
|
|
|
|
|
/* If no callback present or error happened while handling,
|
|
|
|
* an error message will be sent to friend
|
|
|
|
*/
|
|
|
|
if (call->error == MSI_E_NONE) {
|
|
|
|
call->error = MSI_E_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
2023-07-25 11:53:09 +02:00
|
|
|
static MSICall *get_call(MSISession *session, uint32_t friend_number)
|
|
|
|
{
|
|
|
|
assert(session != nullptr);
|
|
|
|
|
|
|
|
if (session->calls == nullptr || session->calls_tail < friend_number) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return session->calls[friend_number];
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
2023-07-25 11:53:09 +02:00
|
|
|
static MSICall *new_call(MSISession *session, uint32_t friend_number)
|
|
|
|
{
|
|
|
|
assert(session != nullptr);
|
|
|
|
|
|
|
|
MSICall *rc = (MSICall *)calloc(1, sizeof(MSICall));
|
|
|
|
|
|
|
|
if (rc == nullptr) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc->session = session;
|
|
|
|
rc->friend_number = friend_number;
|
|
|
|
|
|
|
|
if (session->calls == nullptr) { /* Creating */
|
|
|
|
session->calls = (MSICall **)calloc(friend_number + 1, sizeof(MSICall *));
|
|
|
|
|
|
|
|
if (session->calls == nullptr) {
|
|
|
|
free(rc);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
session->calls_tail = friend_number;
|
|
|
|
session->calls_head = friend_number;
|
|
|
|
} else if (session->calls_tail < friend_number) { /* Appending */
|
Squashed 'external/toxcore/c-toxcore/' changes from e2c01e457b..b03b571272
b03b571272 fix: flaky tcp test This only fixes the symptoms, not the real problem. Sometimes or consistently on some platforms a socket might need a moment before it can be written to.
32e67ab4c2 cleanup: use typedef for private message ID's in callback
7b1db6adc1 feat: add message IDs to private group messages
99e0bcc27d refactor: Observers/ignored peers can now send and receive custom packets
b3c3c49d26 fix: Disable IPv6 in Windows cross-compilation tests
e742deddff feat: Check hashes of Windows dependencies when cross-compiling
dfb9a0b02b fix: Test the current Windows Dockerfile, not an old Dockerhub image
14de93ccec chore: Use WineHQ's Wine as Debian Bookworm's crashes
ed37616249 docs: Update the Windows cross-compilation section
9bb79c174f cleanup: Remove a couple of unnecessary misc_tools dependencies
19475adb70 chore: Statically link OpenMP into the cracker fun util on Windows
1be311e51f feat: Build the fun utils when cross-compiling to Windows
88133f8446 chore: Strip Windows binaries
3cc0ae7535 refactor: Copy over all of the required static dependencies
c4fa8f7fb1 feat: Generate .def, .exp and .lib files when building for Windows
74bbac5363 feat: Let CMake create the dll instead of doing so ourselves
246642e9ae feat: Harden Windows cross-compilation
8d431c0d11 chore: Bump Windows build dependency versions
e519f7998b fix: Remove unnecessary wsock32 dependency on Windows
ed2b60c217 chore: Use a specific non-broken slimcc version.
d7f21010a1 chore: Update github actions.
e71a68b7f2 docs: Update the list of CMake options
77e08876ff chore: Remove mod and founder from group API naming scheme
12bc042767 docs: add the experimental api build option to INSTALL.md
e1fa5cae96 refactor: Rename Queries to Query to align with other enums.
be82a3ea30 fix: Correct type for conference offline peer numbers.
0627c36716 test: Add pkgsrc build.
92578afe4b test: Add FreeBSD VM action on GitHub.
52ece0f57b test: Build toxcore on NetBSD (VM).
3fe8ee2c11 chore: Only install tox_private.h on request.
9a8dfa06ab fix: save_compatibility_test failing on big-endian systems
86f5e55578 fix: Don't serve files from websockify.
710eb674a5 fix: Correctly pass extended public keys to group moderation code.
021db7031c refactor: Use `struct`s for extended public/secret keys.
a1e999fd80 chore: Compile libsodium reference implementation with compcert.
fbe3c19cf5 cleanup: correct a few nullable annotations
623e3ee5c3 cleanup: Don't use `memcpy` to cast arbitrary `struct`s to `uint8_t[]`.
c71567dc18 fix: Pass array, not array pointer, to `memcmp`.
9b46a08144 cleanup: Never pass `void*` directly to `memcpy`.
5d7b7a7bbc refactor: Use tox rng to seed the keypair generation.
961891d568 cleanup: Small improvements found by PVS Studio.
8201019f0d chore: Disable NGC saving by default, enable through Tox_Options.
5dd9ee3f65 cleanup: Replace pointer arithmetic with explicit `&arr[i]`.
ca4606d49d refactor: Use strong typedef for NGC peer id.
442213b722 cleanup: Simplify custom packet length check in NGC.
08d3393def fix: Correct a few potential null derefs in bootstrap daemon.
b9877b32b0 fix: Add missing memunlock of local variable when it goes out of scope.
dab5fe44b9 fix: Zero out stack-allocated secret key before return.
f058103299 refactor: Make prune_gc_sanctions_list more obviously correct.
3ba7a0dec9 docs: Add static analysis tool list to README.
8d0811a0f3 docs: Run prettier-markdown on markdown files.
969e3a2bfc refactor: Fix network test not using the strong typedef
93c83fbc7c refactor: Use strong typedef instead of struct for `Socket`.
9fe18b176f fix: Fix some false positive from PVS Studio.
7c44379ccb cleanup: Check that WINXP macro exists before comparing it.
5c93231bef refactor: Make tox mutex non-recursive.
aacff73939 docs: Fix up doxyfile.
d55fc85ff5 docs: Add more documentation to crypto_core.
5bdaaaedb6 refactor: Remove `Tox *` from `tox_dispatch`.
e202341e76 refactor: Don't rely on tox_dispatch passing tox in tests.
34df938f52 chore: Use C++ mode for clang-tidy.
8b05296a78 chore: Check that both gtest and gmock exist for tests.
42010660e1 test: Add slimcc compiler compatibility test.
b473630321 chore: Add some comments to the astyle config.
b7404f24f6 cleanup: Remove implicit bool conversions.
4e2dba4d9f chore: Reformat sources with astyle.
4359e3a6bc chore: Rename C++ headers to .hh suffixes.
0c05566e58 cleanup: Further `#include` cleanups.
8d29935b7a chore: Only check the bootstrap daemon checksum on release.
f70e588bc6 cleanup: Add more `const` where possible.
511bfe39c8 cleanup: Use Bazel modules to enforce proper `#include` hygiene.
1710a0d091 refactor: Move pack/unpack `IP_Port` from DHT into network module.
a975943564 chore: Really fix coverage docker image build.
c08409390f chore: Fix post-submit coverage image.
39aadf8922 fix: Don't use `memcmp` to compare `IP_Port`s.
d94246a906 fix: partially fix a bug that prevented group part messages from sending.
eeaa039222 chore: Fix rpm build; add a CI check for it.
8328449c1a chore: Speed up docker builds a bit by reducing layer count.
d6d67d56f3 cleanup: Add `const` where possible in auto tests.
6aa9e6850d cleanup: Minor cleanup of event unpack code.
bdf460a3a9 refactor: Rename `system_{memory,...}` to `os_{memory,...}`.
203e1af81e fix: a few off by one errors in group autotests
5c093c4888 cleanup: Remove all uses of `SIZEOF_VLA`.
662c2140f3 test: Add goblint static analyser.
8f07755834 cleanup: Use `memzero(x, s)` instead of `memset(x, 0, s)`.
a7258e40cf cleanup: Use explicit 0 instead of `PACKET_ID_PADDING`.
6370d0f15d cleanup: Expand the `Tox_Options` accessor macros.
14a1a0b9bd cleanup: Remove plan9 support.
a05dccad13 test: Add a simple new/delete test for Tox.
1cdcf938b9 cleanup: Add comment after every `#endif`.
ba99d4dc4b test: Fix comment I broke in the events test PR.
e07248debb refactor: Migrate auto_tests to new events API.
bdd42b5452 refactor: Add common msgpack array packer with callback.
3c659f5288 cleanup: Rename group to conference in groupav documentation.
89957be230 cleanup: Ensure handler params are named after callback params.
c650d9d345 refactor: Pass `this` pointer as first param to s11n callbacks.
e7fb91ddb8 refactor: Allow NULL pointers for byte arrays in events.
5e2c8cabc1 cleanup: make some improvements to group moderation test
259de4867e cleanup: Remove `bin_pack_{new,free}`.
21a8ff5895 cleanup: skip a do_gc iteration before removing peers marked for deletion
16809dc36e feat: Add dht_get_nodes_response event to the events system.
git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: b03b5712720de9a9901ea12fd741f177327a7021
2024-03-07 23:12:55 +01:00
|
|
|
MSICall **tmp = (MSICall **)realloc(session->calls, (friend_number + 1) * sizeof(MSICall *));
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
if (tmp == nullptr) {
|
|
|
|
free(rc);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
session->calls = tmp;
|
|
|
|
|
|
|
|
/* Set fields in between to null */
|
|
|
|
for (uint32_t i = session->calls_tail + 1; i < friend_number; ++i) {
|
|
|
|
session->calls[i] = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc->prev = session->calls[session->calls_tail];
|
|
|
|
session->calls[session->calls_tail]->next = rc;
|
|
|
|
|
|
|
|
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;
|
|
|
|
session->calls_head = friend_number;
|
|
|
|
}
|
|
|
|
|
|
|
|
session->calls[friend_number] = rc;
|
|
|
|
return rc;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
static void kill_call(const Logger *log, MSICall *call)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
/* Assume that session mutex is locked */
|
|
|
|
if (call == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MSISession *session = call->session;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Killing call: %p", (void *)call);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
MSICall *prev = call->prev;
|
|
|
|
MSICall *next = call->next;
|
|
|
|
|
|
|
|
if (prev != nullptr) {
|
|
|
|
prev->next = next;
|
|
|
|
} else if (next != nullptr) {
|
|
|
|
session->calls_head = next->friend_number;
|
|
|
|
} else {
|
|
|
|
goto CLEAR_CONTAINER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (next != nullptr) {
|
|
|
|
next->prev = prev;
|
|
|
|
} else if (prev != nullptr) {
|
|
|
|
session->calls_tail = prev->friend_number;
|
|
|
|
} else {
|
|
|
|
goto CLEAR_CONTAINER;
|
|
|
|
}
|
|
|
|
|
|
|
|
session->calls[call->friend_number] = nullptr;
|
|
|
|
free(call);
|
|
|
|
return;
|
|
|
|
|
|
|
|
CLEAR_CONTAINER:
|
|
|
|
session->calls_head = 0;
|
|
|
|
session->calls_tail = 0;
|
|
|
|
free(session->calls);
|
|
|
|
free(call);
|
|
|
|
session->calls = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
static bool try_handle_init(const Logger *log, MSICall *call, const MSIMessage *msg)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
if (!msg->capabilities.exists) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_WARNING(log, "Session: %p Invalid capabilities on 'init'", (void *)call->session);
|
2023-07-25 11:53:09 +02:00
|
|
|
call->error = MSI_E_INVALID_MESSAGE;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (call->state) {
|
|
|
|
case MSI_CALL_INACTIVE: {
|
|
|
|
/* Call requested */
|
|
|
|
call->peer_capabilities = msg->capabilities.value;
|
|
|
|
call->state = MSI_CALL_REQUESTED;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
if (!invoke_callback(log, call, MSI_ON_INVITE)) {
|
2023-07-25 11:53:09 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MSI_CALL_ACTIVE: {
|
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_INFO(log, "Friend is recalling us");
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
MSIMessage out_msg;
|
|
|
|
msg_init(&out_msg, REQU_PUSH);
|
|
|
|
|
|
|
|
out_msg.capabilities.exists = true;
|
|
|
|
out_msg.capabilities.value = call->self_capabilities;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
send_message(log, call->session->tox, call->friend_number, &out_msg);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
/* If peer changed capabilities during re-call they will
|
|
|
|
* be handled accordingly during the next step
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MSI_CALL_REQUESTED: // fall-through
|
|
|
|
case MSI_CALL_REQUESTING: {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_WARNING(log, "Session: %p Invalid state on 'init'", (void *)call->session);
|
2023-07-25 11:53:09 +02:00
|
|
|
call->error = MSI_E_INVALID_STATE;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
static void handle_init(const Logger *log, MSICall *call, const MSIMessage *msg)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
assert(call != nullptr);
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log,
|
2023-07-25 11:53:09 +02:00
|
|
|
"Session: %p Handling 'init' friend: %d", (void *)call->session, call->friend_number);
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
if (!try_handle_init(log, call, msg)) {
|
|
|
|
send_error(log, call->session->tox, call->friend_number, call->error);
|
|
|
|
kill_call(log, call);
|
2023-07-25 11:53:09 +02:00
|
|
|
}
|
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
assert(call != nullptr);
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Session: %p Handling 'push' friend: %d", (void *)call->session,
|
2023-07-25 11:53:09 +02:00
|
|
|
call->friend_number);
|
|
|
|
|
|
|
|
if (!msg->capabilities.exists) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_WARNING(log, "Session: %p Invalid capabilities on 'push'", (void *)call->session);
|
2023-07-25 11:53:09 +02:00
|
|
|
call->error = MSI_E_INVALID_MESSAGE;
|
|
|
|
goto FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (call->state) {
|
|
|
|
case MSI_CALL_ACTIVE: {
|
|
|
|
if (call->peer_capabilities != msg->capabilities.value) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_INFO(log, "Friend is changing capabilities to: %u", msg->capabilities.value);
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
call->peer_capabilities = msg->capabilities.value;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
if (!invoke_callback(log, call, MSI_ON_CAPABILITIES)) {
|
2023-07-25 11:53:09 +02:00
|
|
|
goto FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MSI_CALL_REQUESTING: {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_INFO(log, "Friend answered our call");
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
/* Call started */
|
|
|
|
call->peer_capabilities = msg->capabilities.value;
|
|
|
|
call->state = MSI_CALL_ACTIVE;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
if (!invoke_callback(log, call, MSI_ON_START)) {
|
2023-07-25 11:53:09 +02:00
|
|
|
goto FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MSI_CALL_INACTIVE: // fall-through
|
|
|
|
case MSI_CALL_REQUESTED: {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_WARNING(log, "Ignoring invalid push");
|
2023-07-25 11:53:09 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
FAILURE:
|
2024-11-09 13:44:30 +01:00
|
|
|
send_error(log, call->session->tox, call->friend_number, call->error);
|
|
|
|
kill_call(log, call);
|
2023-07-25 11:53:09 +02:00
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
static void handle_pop(const Logger *log, MSICall *call, const MSIMessage *msg)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
|
|
|
assert(call != nullptr);
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Session: %p Handling 'pop', friend id: %d", (void *)call->session,
|
2023-07-25 11:53:09 +02:00
|
|
|
call->friend_number);
|
|
|
|
|
|
|
|
/* callback errors are ignored */
|
|
|
|
|
|
|
|
if (msg->error.exists) {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_WARNING(log, "Friend detected an error: %d", msg->error.value);
|
2023-07-25 11:53:09 +02:00
|
|
|
call->error = msg->error.value;
|
2024-11-09 13:44:30 +01:00
|
|
|
invoke_callback(log, call, MSI_ON_ERROR);
|
2023-07-25 11:53:09 +02:00
|
|
|
} else {
|
|
|
|
switch (call->state) {
|
|
|
|
case MSI_CALL_INACTIVE: {
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_FATAL(log, "Handling what should be impossible case");
|
2023-12-24 12:21:34 +01:00
|
|
|
break;
|
2023-07-25 11:53:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case MSI_CALL_ACTIVE: {
|
|
|
|
/* Hangup */
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_INFO(log, "Friend hung up on us");
|
|
|
|
invoke_callback(log, call, MSI_ON_END);
|
2023-07-25 11:53:09 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MSI_CALL_REQUESTING: {
|
|
|
|
/* Reject */
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_INFO(log, "Friend rejected our call");
|
|
|
|
invoke_callback(log, call, MSI_ON_END);
|
2023-07-25 11:53:09 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MSI_CALL_REQUESTED: {
|
|
|
|
/* Cancel */
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_INFO(log, "Friend canceled call invite");
|
|
|
|
invoke_callback(log, call, MSI_ON_END);
|
2023-07-25 11:53:09 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
kill_call(log, call);
|
2023-07-25 11:53:09 +02:00
|
|
|
}
|
2024-11-09 13:44:30 +01:00
|
|
|
|
|
|
|
static void handle_msi_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length,
|
|
|
|
void *user_data)
|
2023-07-25 11:53:09 +02:00
|
|
|
{
|
2024-11-09 13:44:30 +01:00
|
|
|
const ToxAV *toxav = (ToxAV *)tox_get_av_object(tox);
|
|
|
|
|
|
|
|
if (toxav == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Logger *log = toxav_get_logger(toxav);
|
|
|
|
|
|
|
|
if (length < 2) {
|
|
|
|
LOGGER_ERROR(log, "MSI packet is less than 2 bytes in size");
|
|
|
|
// we need more than the ID byte for MSI messages
|
|
|
|
return;
|
|
|
|
}
|
2024-01-09 16:39:05 +01:00
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
const uint16_t payload_length = (uint16_t)(length - 1);
|
|
|
|
|
|
|
|
// Zoff: do not show the first byte, its always "PACKET_ID_MSI"
|
|
|
|
const uint8_t *data_strip_id_byte = data + 1;
|
|
|
|
|
|
|
|
LOGGER_DEBUG(log, "Got msi message");
|
|
|
|
|
|
|
|
MSISession *session = tox_av_msi_get(toxav);
|
|
|
|
|
|
|
|
if (session == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
MSIMessage msg;
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
if (msg_parse_in(log, &msg, data_strip_id_byte, payload_length) == -1) {
|
|
|
|
LOGGER_WARNING(log, "Error parsing message");
|
|
|
|
send_error(log, tox, friend_number, MSI_E_INVALID_MESSAGE);
|
2023-07-25 11:53:09 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-11-09 13:44:30 +01:00
|
|
|
LOGGER_DEBUG(log, "Successfully parsed message");
|
2023-07-25 11:53:09 +02:00
|
|
|
|
|
|
|
pthread_mutex_lock(session->mutex);
|
|
|
|
MSICall *call = get_call(session, friend_number);
|
|
|
|
|
|
|
|
if (call == nullptr) {
|
|
|
|
if (msg.request.value != REQU_INIT) {
|
2024-11-09 13:44:30 +01:00
|
|
|
send_error(log, tox, friend_number, MSI_E_STRAY_MESSAGE);
|
2023-07-25 11:53:09 +02:00
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
call = new_call(session, friend_number);
|
|
|
|
|
|
|
|
if (call == nullptr) {
|
2024-11-09 13:44:30 +01:00
|
|
|
send_error(log, tox, friend_number, MSI_E_SYSTEM);
|
2023-07-25 11:53:09 +02:00
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (msg.request.value) {
|
|
|
|
case REQU_INIT: {
|
2024-11-09 13:44:30 +01:00
|
|
|
handle_init(log, call, &msg);
|
2023-07-25 11:53:09 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case REQU_PUSH: {
|
2024-11-09 13:44:30 +01:00
|
|
|
handle_push(log, call, &msg);
|
2023-07-25 11:53:09 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case REQU_POP: {
|
2024-11-09 13:44:30 +01:00
|
|
|
handle_pop(log, call, &msg); /* always kills the call */
|
2023-07-25 11:53:09 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_unlock(session->mutex);
|
|
|
|
}
|