summaryrefslogtreecommitdiff
path: root/protocols/Tox/libtox/src/toxcore/Messenger.c
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Tox/libtox/src/toxcore/Messenger.c')
-rw-r--r--protocols/Tox/libtox/src/toxcore/Messenger.c882
1 files changed, 640 insertions, 242 deletions
diff --git a/protocols/Tox/libtox/src/toxcore/Messenger.c b/protocols/Tox/libtox/src/toxcore/Messenger.c
index 6042fd725b..118c7d946c 100644
--- a/protocols/Tox/libtox/src/toxcore/Messenger.c
+++ b/protocols/Tox/libtox/src/toxcore/Messenger.c
@@ -14,7 +14,10 @@
#include <string.h>
#include <time.h>
+#include "DHT.h"
#include "ccompat.h"
+#include "group_chats.h"
+#include "group_onion_announce.h"
#include "logger.h"
#include "mono_time.h"
#include "network.h"
@@ -26,6 +29,16 @@ static_assert(MAX_CONCURRENT_FILE_PIPES <= UINT8_MAX + 1,
static const Friend empty_friend = {{0}};
+/**
+ * Determines if the friendnumber passed is valid in the Messenger object.
+ *
+ * @param friendnumber The index in the friend list.
+ */
+bool friend_is_valid(const Messenger *m, int32_t friendnumber)
+{
+ return (uint32_t)friendnumber < m->numfriends && m->friendlist[friendnumber].status != 0;
+}
+
/** @brief Set the size of the friend list to numfriends.
*
* @retval -1 if realloc fails.
@@ -107,15 +120,11 @@ void getaddress(const Messenger *m, uint8_t *address)
}
non_null()
-static bool send_online_packet(Messenger *m, int32_t friendnumber)
+static bool send_online_packet(Messenger *m, int friendcon_id)
{
- if (!m_friend_exists(m, friendnumber)) {
- return false;
- }
-
uint8_t packet = PACKET_ID_ONLINE;
- return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
- m->friendlist[friendnumber].friendcon_id), &packet, sizeof(packet), false) != -1;
+ return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, friendcon_id), &packet,
+ sizeof(packet), false) != -1;
}
non_null()
@@ -174,7 +183,7 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta
}
if (friend_con_connected(m->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) {
- send_online_packet(m, i);
+ send_online_packet(m, friendcon_id);
}
return i;
@@ -184,6 +193,20 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta
return FAERR_NOMEM;
}
+non_null()
+static int32_t m_add_friend_contact_norequest(Messenger *m, const uint8_t *real_pk)
+{
+ if (getfriend_id(m, real_pk) != -1) {
+ return FAERR_ALREADYSENT;
+ }
+
+ if (pk_equal(real_pk, nc_get_self_public_key(m->net_crypto))) {
+ return FAERR_OWNKEY;
+ }
+
+ return init_new_friend(m, real_pk, FRIEND_CONFIRMED);
+}
+
/**
* Add a friend.
*
@@ -268,10 +291,6 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u
int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk)
{
- if (getfriend_id(m, real_pk) != -1) {
- return FAERR_ALREADYSENT;
- }
-
if (!public_key_valid(real_pk)) {
return FAERR_BADCHECKSUM;
}
@@ -280,7 +299,7 @@ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk)
return FAERR_OWNKEY;
}
- return init_new_friend(m, real_pk, FRIEND_CONFIRMED);
+ return m_add_friend_contact_norequest(m, real_pk);
}
non_null()
@@ -344,6 +363,53 @@ static int friend_received_packet(const Messenger *m, int32_t friendnumber, uint
m->friendlist[friendnumber].friendcon_id), number);
}
+bool m_create_group_connection(Messenger *m, GC_Chat *chat)
+{
+ random_bytes(m->rng, chat->m_group_public_key, CRYPTO_PUBLIC_KEY_SIZE);
+ const int friendcon_id = new_friend_connection(m->fr_c, chat->m_group_public_key);
+
+ if (friendcon_id == -1) {
+ return false;
+ }
+
+ const Friend_Conn *connection = get_conn(m->fr_c, friendcon_id);
+
+ if (connection == nullptr) {
+ return false;
+ }
+
+ chat->friend_connection_id = friendcon_id;
+
+ if (friend_con_connected(m->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) {
+ send_online_packet(m, friendcon_id);
+ }
+
+ const int onion_friend_number = friend_conn_get_onion_friendnum(connection);
+ Onion_Friend *onion_friend = onion_get_friend(m->onion_c, (uint16_t)onion_friend_number);
+
+ onion_friend_set_gc_public_key(onion_friend, get_chat_id(chat->chat_public_key));
+ onion_friend_set_gc_data(onion_friend, nullptr, 0);
+
+ return true;
+}
+
+/**
+ * Kills the friend connection for a groupchat.
+ */
+void m_kill_group_connection(Messenger *m, const GC_Chat *chat)
+{
+ remove_request_received(m->fr, chat->m_group_public_key);
+
+ friend_connection_callbacks(m->fr_c, chat->friend_connection_id, MESSENGER_CALLBACK_INDEX, nullptr,
+ nullptr, nullptr, nullptr, 0);
+
+ if (friend_con_connected(m->fr_c, chat->friend_connection_id) == FRIENDCONN_STATUS_CONNECTED) {
+ send_offline_packet(m, chat->friend_connection_id);
+ }
+
+ kill_friend_connection(m->fr_c, chat->friend_connection_id);
+}
+
non_null(1) nullable(3)
static int do_receipts(Messenger *m, int32_t friendnumber, void *userdata)
{
@@ -991,6 +1057,11 @@ void m_callback_conference_invite(Messenger *m, m_conference_invite_cb *function
m->conference_invite = function;
}
+/** @brief the callback for group invites. */
+void m_callback_group_invite(Messenger *m, m_group_invite_cb *function)
+{
+ m->group_invite = function;
+}
/** @brief Send a conference invite packet.
*
@@ -1002,6 +1073,17 @@ bool send_conference_invite_packet(const Messenger *m, int32_t friendnumber, con
return write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_CONFERENCE, data, length, false);
}
+
+/** @brief Send a group invite packet.
+ *
+ * @retval true if success
+ */
+bool send_group_invite_packet(const Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length)
+{
+ return write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_GROUPCHAT, data, length, false);
+}
+
+
/*** FILE SENDING */
@@ -1922,12 +2004,13 @@ static void check_friend_request_timed_out(Messenger *m, uint32_t i, uint64_t t,
}
}
+non_null(1) nullable(4)
static int m_handle_status(void *object, int i, bool status, void *userdata)
{
Messenger *m = (Messenger *)object;
if (status) { /* Went online. */
- send_online_packet(m, i);
+ send_online_packet(m, m->friendlist[i].friendcon_id);
} else { /* Went offline. */
if (m->friendlist[i].status == FRIEND_ONLINE) {
set_friend_status(m, i, FRIEND_CONFIRMED, userdata);
@@ -1937,321 +2020,396 @@ static int m_handle_status(void *object, int i, bool status, void *userdata)
return 0;
}
-static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t len, void *userdata)
+non_null(1, 3) nullable(5)
+static int m_handle_packet_offline(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
{
- if (len == 0) {
- return -1;
+ if (data_length == 0) {
+ set_friend_status(m, i, FRIEND_CONFIRMED, userdata);
}
- Messenger *m = (Messenger *)object;
- const uint8_t packet_id = temp[0];
- const uint8_t *data = temp + 1;
- const uint16_t data_length = len - 1;
+ return 0;
+}
- if (m->friendlist[i].status != FRIEND_ONLINE) {
- if (packet_id == PACKET_ID_ONLINE && len == 1) {
- set_friend_status(m, i, FRIEND_ONLINE, userdata);
- send_online_packet(m, i);
- } else {
- return -1;
- }
+non_null(1, 3) nullable(5)
+static int m_handle_packet_nickname(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+ if (data_length > MAX_NAME_LENGTH) {
+ return 0;
}
- switch (packet_id) {
- case PACKET_ID_OFFLINE: {
- if (data_length > 0) {
- break;
- }
-
- set_friend_status(m, i, FRIEND_CONFIRMED, userdata);
- break;
- }
+ /* Make sure the NULL terminator is present. */
+ VLA(uint8_t, data_terminated, data_length + 1);
+ memcpy(data_terminated, data, data_length);
+ data_terminated[data_length] = 0;
- case PACKET_ID_NICKNAME: {
- if (data_length > MAX_NAME_LENGTH) {
- break;
- }
+ /* inform of namechange before we overwrite the old name */
+ if (m->friend_namechange != nullptr) {
+ m->friend_namechange(m, i, data_terminated, data_length, userdata);
+ }
- /* Make sure the NULL terminator is present. */
- VLA(uint8_t, data_terminated, data_length + 1);
- memcpy(data_terminated, data, data_length);
- data_terminated[data_length] = 0;
+ memcpy(m->friendlist[i].name, data_terminated, data_length);
+ m->friendlist[i].name_length = data_length;
- /* inform of namechange before we overwrite the old name */
- if (m->friend_namechange != nullptr) {
- m->friend_namechange(m, i, data_terminated, data_length, userdata);
- }
+ return 0;
+}
- memcpy(m->friendlist[i].name, data_terminated, data_length);
- m->friendlist[i].name_length = data_length;
+non_null(1, 3) nullable(5)
+static int m_handle_packet_statusmessage(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+ if (data_length > MAX_STATUSMESSAGE_LENGTH) {
+ return 0;
+ }
- break;
- }
+ /* Make sure the NULL terminator is present. */
+ VLA(uint8_t, data_terminated, data_length + 1);
+ memcpy(data_terminated, data, data_length);
+ data_terminated[data_length] = 0;
- case PACKET_ID_STATUSMESSAGE: {
- if (data_length > MAX_STATUSMESSAGE_LENGTH) {
- break;
- }
+ if (m->friend_statusmessagechange != nullptr) {
+ m->friend_statusmessagechange(m, i, data_terminated, data_length, userdata);
+ }
- /* Make sure the NULL terminator is present. */
- VLA(uint8_t, data_terminated, data_length + 1);
- memcpy(data_terminated, data, data_length);
- data_terminated[data_length] = 0;
+ set_friend_statusmessage(m, i, data_terminated, data_length);
- if (m->friend_statusmessagechange != nullptr) {
- m->friend_statusmessagechange(m, i, data_terminated, data_length, userdata);
- }
+ return 0;
+}
- set_friend_statusmessage(m, i, data_terminated, data_length);
- break;
- }
+non_null(1, 3) nullable(5)
+static int m_handle_packet_userstatus(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+ if (data_length != 1) {
+ return 0;
+ }
- case PACKET_ID_USERSTATUS: {
- if (data_length != 1) {
- break;
- }
+ const Userstatus status = (Userstatus)data[0];
- const Userstatus status = (Userstatus)data[0];
+ if (status >= USERSTATUS_INVALID) {
+ return 0;
+ }
- if (status >= USERSTATUS_INVALID) {
- break;
- }
+ if (m->friend_userstatuschange != nullptr) {
+ m->friend_userstatuschange(m, i, status, userdata);
+ }
- if (m->friend_userstatuschange != nullptr) {
- m->friend_userstatuschange(m, i, status, userdata);
- }
+ set_friend_userstatus(m, i, status);
- set_friend_userstatus(m, i, status);
- break;
- }
+ return 0;
+}
- case PACKET_ID_TYPING: {
- if (data_length != 1) {
- break;
- }
+non_null(1, 3) nullable(5)
+static int m_handle_packet_typing(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+ if (data_length != 1) {
+ return 0;
+ }
- const bool typing = data[0] != 0;
+ const bool typing = data[0] != 0;
- set_friend_typing(m, i, typing);
+ set_friend_typing(m, i, typing);
- if (m->friend_typingchange != nullptr) {
- m->friend_typingchange(m, i, typing, userdata);
- }
+ if (m->friend_typingchange != nullptr) {
+ m->friend_typingchange(m, i, typing, userdata);
+ }
- break;
- }
+ return 0;
+}
- case PACKET_ID_MESSAGE: // fall-through
- case PACKET_ID_ACTION: {
- if (data_length == 0) {
- break;
- }
+non_null(1, 3) nullable(6)
+static int m_handle_packet_message(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, const Message_Type message_type, void *userdata)
+{
+ if (data_length == 0) {
+ return 0;
+ }
- const uint8_t *message = data;
- const uint16_t message_length = data_length;
+ const uint8_t *message = data;
+ const uint16_t message_length = data_length;
- /* Make sure the NULL terminator is present. */
- VLA(uint8_t, message_terminated, message_length + 1);
- memcpy(message_terminated, message, message_length);
- message_terminated[message_length] = 0;
- const uint8_t type = packet_id - PACKET_ID_MESSAGE;
+ /* Make sure the NULL terminator is present. */
+ VLA(uint8_t, message_terminated, message_length + 1);
+ memcpy(message_terminated, message, message_length);
+ message_terminated[message_length] = 0;
- if (m->friend_message != nullptr) {
- m->friend_message(m, i, type, message_terminated, message_length, userdata);
- }
+ if (m->friend_message != nullptr) {
+ m->friend_message(m, i, message_type, message_terminated, message_length, userdata);
+ }
- break;
- }
+ return 0;
+}
- case PACKET_ID_INVITE_CONFERENCE: {
- if (data_length == 0) {
- break;
- }
+non_null(1, 3) nullable(5)
+static int m_handle_packet_invite_conference(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+ if (data_length == 0) {
+ return 0;
+ }
- if (m->conference_invite != nullptr) {
- m->conference_invite(m, i, data, data_length, userdata);
- }
+ if (m->conference_invite != nullptr) {
+ m->conference_invite(m, i, data, data_length, userdata);
+ }
- break;
- }
+ return 0;
+}
- case PACKET_ID_FILE_SENDREQUEST: {
- const unsigned int head_length = 1 + sizeof(uint32_t) + sizeof(uint64_t) + FILE_ID_LENGTH;
+non_null(1, 3) nullable(5)
+static int m_handle_packet_file_sendrequest(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+ const unsigned int head_length = 1 + sizeof(uint32_t) + sizeof(uint64_t) + FILE_ID_LENGTH;
- if (data_length < head_length) {
- break;
- }
+ if (data_length < head_length) {
+ return 0;
+ }
- const uint8_t filenumber = data[0];
+ const uint8_t filenumber = data[0];
#if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES
- if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
- break;
- }
+ if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
+ return 0;
+ }
#endif
- uint64_t filesize;
- uint32_t file_type;
- const uint16_t filename_length = data_length - head_length;
+ uint64_t filesize;
+ uint32_t file_type;
+ const uint16_t filename_length = data_length - head_length;
- if (filename_length > MAX_FILENAME_LENGTH) {
- break;
- }
+ if (filename_length > MAX_FILENAME_LENGTH) {
+ return 0;
+ }
- memcpy(&file_type, data + 1, sizeof(file_type));
- file_type = net_ntohl(file_type);
+ memcpy(&file_type, data + 1, sizeof(file_type));
+ file_type = net_ntohl(file_type);
- net_unpack_u64(data + 1 + sizeof(uint32_t), &filesize);
- struct File_Transfers *ft = &m->friendlist[i].file_receiving[filenumber];
+ net_unpack_u64(data + 1 + sizeof(uint32_t), &filesize);
+ struct File_Transfers *ft = &m->friendlist[i].file_receiving[filenumber];
- if (ft->status != FILESTATUS_NONE) {
- break;
- }
+ if (ft->status != FILESTATUS_NONE) {
+ return 0;
+ }
- ft->status = FILESTATUS_NOT_ACCEPTED;
- ft->size = filesize;
- ft->transferred = 0;
- ft->paused = FILE_PAUSE_NOT;
- memcpy(ft->id, data + 1 + sizeof(uint32_t) + sizeof(uint64_t), FILE_ID_LENGTH);
+ ft->status = FILESTATUS_NOT_ACCEPTED;
+ ft->size = filesize;
+ ft->transferred = 0;
+ ft->paused = FILE_PAUSE_NOT;
+ memcpy(ft->id, data + 1 + sizeof(uint32_t) + sizeof(uint64_t), FILE_ID_LENGTH);
- VLA(uint8_t, filename_terminated, filename_length + 1);
- const uint8_t *filename = nullptr;
+ VLA(uint8_t, filename_terminated, filename_length + 1);
+ const uint8_t *filename = nullptr;
- if (filename_length > 0) {
- /* Force NULL terminate file name. */
- memcpy(filename_terminated, data + head_length, filename_length);
- filename_terminated[filename_length] = 0;
- filename = filename_terminated;
- }
+ if (filename_length > 0) {
+ /* Force NULL terminate file name. */
+ memcpy(filename_terminated, data + head_length, filename_length);
+ filename_terminated[filename_length] = 0;
+ filename = filename_terminated;
+ }
- uint32_t real_filenumber = filenumber;
- real_filenumber += 1;
- real_filenumber <<= 16;
+ uint32_t real_filenumber = filenumber;
+ real_filenumber += 1;
+ real_filenumber <<= 16;
- if (m->file_sendrequest != nullptr) {
- m->file_sendrequest(m, i, real_filenumber, file_type, filesize, filename, filename_length,
- userdata);
- }
+ if (m->file_sendrequest != nullptr) {
+ m->file_sendrequest(m, i, real_filenumber, file_type, filesize, filename, filename_length,
+ userdata);
+ }
- break;
- }
+ return 0;
+}
- case PACKET_ID_FILE_CONTROL: {
- if (data_length < 3) {
- break;
- }
+non_null(1, 3) nullable(5)
+static int m_handle_packet_file_control(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+ if (data_length < 3) {
+ return 0;
+ }
- // On the other side, "outbound" is "inbound", i.e. if they send 1,
- // that means "inbound" on their side, but we call it "outbound"
- // here.
- const bool outbound = data[0] == 1;
- uint8_t filenumber = data[1];
- const uint8_t control_type = data[2];
+ // On the other side, "outbound" is "inbound", i.e. if they send 1,
+ // that means "inbound" on their side, but we call it "outbound"
+ // here.
+ const bool outbound = data[0] == 1;
+ const uint8_t filenumber = data[1];
+ const uint8_t control_type = data[2];
#if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES
- if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
- break;
- }
+ if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
+ return 0;
+ }
#endif
- if (handle_filecontrol(m, i, outbound, filenumber, control_type, data + 3, data_length - 3, userdata) == -1) {
- // TODO(iphydf): Do something different here? Right now, this
- // check is pointless.
- break;
- }
+ if (handle_filecontrol(m, i, outbound, filenumber, control_type, data + 3, data_length - 3, userdata) == -1) {
+ // TODO(iphydf): Do something different here? Right now, this
+ // check is pointless.
+ return 0;
+ }
- break;
- }
+ return 0;
+}
- case PACKET_ID_FILE_DATA: {
- if (data_length < 1) {
- break;
- }
+non_null(1, 3) nullable(5)
+static int m_handle_packet_file_data(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+ if (data_length < 1) {
+ return 0;
+ }
- uint8_t filenumber = data[0];
+ const uint8_t filenumber = data[0];
#if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES
- if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
- break;
- }
+ if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
+ return 0;
+ }
#endif
- struct File_Transfers *ft = &m->friendlist[i].file_receiving[filenumber];
+ struct File_Transfers *ft = &m->friendlist[i].file_receiving[filenumber];
- if (ft->status != FILESTATUS_TRANSFERRING) {
- break;
- }
+ if (ft->status != FILESTATUS_TRANSFERRING) {
+ return 0;
+ }
- uint64_t position = ft->transferred;
- uint32_t real_filenumber = filenumber;
- real_filenumber += 1;
- real_filenumber <<= 16;
- uint16_t file_data_length = data_length - 1;
- const uint8_t *file_data;
+ uint64_t position = ft->transferred;
+ uint32_t real_filenumber = filenumber;
+ real_filenumber += 1;
+ real_filenumber <<= 16;
+ uint16_t file_data_length = data_length - 1;
+ const uint8_t *file_data;
- if (file_data_length == 0) {
- file_data = nullptr;
- } else {
- file_data = data + 1;
- }
+ if (file_data_length == 0) {
+ file_data = nullptr;
+ } else {
+ file_data = data + 1;
+ }
- /* Prevent more data than the filesize from being passed to clients. */
- if ((ft->transferred + file_data_length) > ft->size) {
- file_data_length = ft->size - ft->transferred;
- }
+ /* Prevent more data than the filesize from being passed to clients. */
+ if ((ft->transferred + file_data_length) > ft->size) {
+ file_data_length = ft->size - ft->transferred;
+ }
- if (m->file_filedata != nullptr) {
- m->file_filedata(m, i, real_filenumber, position, file_data, file_data_length, userdata);
- }
+ if (m->file_filedata != nullptr) {
+ m->file_filedata(m, i, real_filenumber, position, file_data, file_data_length, userdata);
+ }
- ft->transferred += file_data_length;
+ ft->transferred += file_data_length;
- if (file_data_length > 0 && (ft->transferred >= ft->size || file_data_length != MAX_FILE_DATA_SIZE)) {
- file_data_length = 0;
- file_data = nullptr;
- position = ft->transferred;
+ if (file_data_length > 0 && (ft->transferred >= ft->size || file_data_length != MAX_FILE_DATA_SIZE)) {
+ file_data_length = 0;
+ file_data = nullptr;
+ position = ft->transferred;
- /* Full file received. */
- if (m->file_filedata != nullptr) {
- m->file_filedata(m, i, real_filenumber, position, file_data, file_data_length, userdata);
- }
- }
+ /* Full file received. */
+ if (m->file_filedata != nullptr) {
+ m->file_filedata(m, i, real_filenumber, position, file_data, file_data_length, userdata);
+ }
+ }
- /* Data is zero, filetransfer is over. */
- if (file_data_length == 0) {
- ft->status = FILESTATUS_NONE;
- }
+ /* Data is zero, filetransfer is over. */
+ if (file_data_length == 0) {
+ ft->status = FILESTATUS_NONE;
+ }
- break;
- }
+ return 0;
+}
- case PACKET_ID_MSI: {
- if (data_length == 0) {
- break;
- }
+non_null(1, 3) nullable(5)
+static int m_handle_packet_msi(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+ if (data_length == 0) {
+ return 0;
+ }
- if (m->msi_packet != nullptr) {
- m->msi_packet(m, i, data, data_length, m->msi_packet_userdata);
- }
+ if (m->msi_packet != nullptr) {
+ m->msi_packet(m, i, data, data_length, m->msi_packet_userdata);
+ }
- break;
- }
+ return 0;
+}
- default: {
- handle_custom_lossless_packet(object, i, temp, len, userdata);
- break;
+non_null(1, 3) nullable(5)
+static int m_handle_packet_invite_groupchat(Messenger *m, const int i, const uint8_t *data, const uint16_t data_length, void *userdata)
+{
+#ifndef VANILLA_NACL
+
+ // first two bytes are messenger packet type and group invite type
+ if (data_length < 2 + GC_JOIN_DATA_LENGTH) {
+ return 0;
+ }
+
+ const uint8_t invite_type = data[1];
+ const uint8_t *join_data = data + 2;
+ const uint32_t join_data_len = data_length - 2;
+
+ if (m->group_invite != nullptr && data[1] == GROUP_INVITE && data_length != 2 + GC_JOIN_DATA_LENGTH) {
+ if (group_not_added(m->group_handler, join_data, join_data_len)) {
+ m->group_invite(m, i, join_data, GC_JOIN_DATA_LENGTH,
+ join_data + GC_JOIN_DATA_LENGTH, join_data_len - GC_JOIN_DATA_LENGTH, userdata);
}
+ } else if (invite_type == GROUP_INVITE_ACCEPTED) {
+ handle_gc_invite_accepted_packet(m->group_handler, i, join_data, join_data_len);
+ } else if (invite_type == GROUP_INVITE_CONFIRMATION) {
+ handle_gc_invite_confirmed_packet(m->group_handler, i, join_data, join_data_len);
}
+#endif // VANILLA_NACL
+
return 0;
}
+non_null(1, 3) nullable(5)
+static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t len, void *userdata)
+{
+ if (len == 0) {
+ return -1;
+ }
+
+ Messenger *m = (Messenger *)object;
+ const uint8_t packet_id = temp[0];
+ const uint8_t *data = temp + 1;
+ const uint16_t data_length = len - 1;
+
+ if (m->friendlist[i].status != FRIEND_ONLINE) {
+ if (packet_id == PACKET_ID_ONLINE && len == 1) {
+ set_friend_status(m, i, FRIEND_ONLINE, userdata);
+ send_online_packet(m, m->friendlist[i].friendcon_id);
+ } else {
+ return -1;
+ }
+ }
+
+ switch (packet_id) {
+ // TODO(Green-Sky): now all return 0 on error AND success, make errors errors?
+ case PACKET_ID_OFFLINE:
+ return m_handle_packet_offline(m, i, data, data_length, userdata);
+ case PACKET_ID_NICKNAME:
+ return m_handle_packet_nickname(m, i, data, data_length, userdata);
+ case PACKET_ID_STATUSMESSAGE:
+ return m_handle_packet_statusmessage(m, i, data, data_length, userdata);
+ case PACKET_ID_USERSTATUS:
+ return m_handle_packet_userstatus(m, i, data, data_length, userdata);
+ case PACKET_ID_TYPING:
+ return m_handle_packet_typing(m, i, data, data_length, userdata);
+ case PACKET_ID_MESSAGE:
+ return m_handle_packet_message(m, i, data, data_length, MESSAGE_NORMAL, userdata);
+ case PACKET_ID_ACTION:
+ return m_handle_packet_message(m, i, data, data_length, MESSAGE_ACTION, userdata);
+ case PACKET_ID_INVITE_CONFERENCE:
+ return m_handle_packet_invite_conference(m, i, data, data_length, userdata);
+ case PACKET_ID_FILE_SENDREQUEST:
+ return m_handle_packet_file_sendrequest(m, i, data, data_length, userdata);
+ case PACKET_ID_FILE_CONTROL:
+ return m_handle_packet_file_control(m, i, data, data_length, userdata);
+ case PACKET_ID_FILE_DATA:
+ return m_handle_packet_file_data(m, i, data, data_length, userdata);
+ case PACKET_ID_MSI:
+ return m_handle_packet_msi(m, i, data, data_length, userdata);
+ case PACKET_ID_INVITE_GROUPCHAT:
+ return m_handle_packet_invite_groupchat(m, i, data, data_length, userdata);
+ }
+
+ return handle_custom_lossless_packet(object, i, temp, len, userdata);
+}
+
non_null(1) nullable(2)
static void do_friends(Messenger *m, void *userdata)
{
@@ -2370,6 +2528,89 @@ uint32_t messenger_run_interval(const Messenger *m)
return crypto_interval;
}
+/** @brief Attempts to create a DHT announcement for a group chat with our connection info. An
+ * announcement can only be created if we either have a UDP or TCP connection to the network.
+ *
+ * @retval true if success.
+ */
+#ifndef VANILLA_NACL
+non_null()
+static bool self_announce_group(const Messenger *m, GC_Chat *chat, Onion_Friend *onion_friend)
+{
+ GC_Public_Announce announce = {{{{{0}}}}};
+
+ const bool ip_port_is_set = chat->self_udp_status != SELF_UDP_STATUS_NONE;
+ const int tcp_num = tcp_copy_connected_relays(chat->tcp_conn, announce.base_announce.tcp_relays,
+ GCA_MAX_ANNOUNCED_TCP_RELAYS);
+
+ if (tcp_num == 0 && !ip_port_is_set) {
+ onion_friend_set_gc_data(onion_friend, nullptr, 0);
+ return false;
+ }
+
+ announce.base_announce.tcp_relays_count = (uint8_t)tcp_num;
+ announce.base_announce.ip_port_is_set = (uint8_t)(ip_port_is_set ? 1 : 0);
+
+ if (ip_port_is_set) {
+ memcpy(&announce.base_announce.ip_port, &chat->self_ip_port, sizeof(IP_Port));
+ }
+
+ memcpy(announce.base_announce.peer_public_key, chat->self_public_key, ENC_PUBLIC_KEY_SIZE);
+ memcpy(announce.chat_public_key, get_chat_id(chat->chat_public_key), ENC_PUBLIC_KEY_SIZE);
+
+ uint8_t gc_data[GCA_MAX_DATA_LENGTH];
+ const int length = gca_pack_public_announce(m->log, gc_data, GCA_MAX_DATA_LENGTH, &announce);
+
+ if (length <= 0) {
+ onion_friend_set_gc_data(onion_friend, nullptr, 0);
+ return false;
+ }
+
+ if (gca_add_announce(m->mono_time, m->group_announce, &announce) == nullptr) {
+ onion_friend_set_gc_data(onion_friend, nullptr, 0);
+ return false;
+ }
+
+ onion_friend_set_gc_data(onion_friend, gc_data, (uint16_t)length);
+ chat->update_self_announces = false;
+ chat->last_time_self_announce = mono_time_get(chat->mono_time);
+
+ if (tcp_num > 0) {
+ pk_copy(chat->announced_tcp_relay_pk, announce.base_announce.tcp_relays[0].public_key);
+ } else {
+ memset(chat->announced_tcp_relay_pk, 0, sizeof(chat->announced_tcp_relay_pk));
+ }
+
+ LOGGER_DEBUG(chat->log, "Published group announce. TCP relays: %d, UDP status: %d", tcp_num,
+ chat->self_udp_status);
+ return true;
+}
+
+non_null()
+static void do_gc_onion_friends(const Messenger *m)
+{
+ const uint16_t num_friends = onion_get_friend_count(m->onion_c);
+
+ for (uint16_t i = 0; i < num_friends; ++i) {
+ Onion_Friend *onion_friend = onion_get_friend(m->onion_c, i);
+
+ if (!onion_friend_is_groupchat(onion_friend)) {
+ continue;
+ }
+
+ GC_Chat *chat = gc_get_group_by_public_key(m->group_handler, onion_friend_get_gc_public_key(onion_friend));
+
+ if (chat == nullptr) {
+ continue;
+ }
+
+ if (chat->update_self_announces) {
+ self_announce_group(m, chat, onion_friend);
+ }
+ }
+}
+#endif // VANILLA_NACL
+
/** @brief The main loop that needs to be run at least 20 times per second. */
void do_messenger(Messenger *m, void *userdata)
{
@@ -2406,6 +2647,11 @@ void do_messenger(Messenger *m, void *userdata)
do_onion_client(m->onion_c);
do_friend_connections(m->fr_c, userdata);
do_friends(m, userdata);
+#ifndef VANILLA_NACL
+ do_gc(m->group_handler, userdata);
+ do_gca(m->mono_time, m->group_announce);
+ do_gc_onion_friends(m);
+#endif
m_connection_status_callback(m, userdata);
if (mono_time_get(m->mono_time) > m->lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) {
@@ -2886,6 +3132,101 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui
return STATE_LOAD_STATUS_CONTINUE;
}
+#ifndef VANILLA_NACL
+non_null()
+static void pack_groupchats(const GC_Session *c, Bin_Pack *bp)
+{
+ assert(bp != nullptr && c != nullptr);
+ bin_pack_array(bp, gc_count_groups(c));
+
+ for (uint32_t i = 0; i < c->chats_index; ++i) { // this loop must match the one in gc_count_groups()
+ const GC_Chat *chat = &c->chats[i];
+
+ if (!gc_group_is_valid(chat)) {
+ continue;
+ }
+
+ gc_group_save(chat, bp);
+ }
+}
+
+non_null()
+static bool pack_groupchats_handler(Bin_Pack *bp, const void *obj)
+{
+ pack_groupchats((const GC_Session *)obj, bp);
+ return true; // TODO(iphydf): Return bool from pack functions.
+}
+
+non_null()
+static uint32_t saved_groups_size(const Messenger *m)
+{
+ GC_Session *c = m->group_handler;
+ return bin_pack_obj_size(pack_groupchats_handler, c);
+}
+
+non_null()
+static uint8_t *groups_save(const Messenger *m, uint8_t *data)
+{
+ const GC_Session *c = m->group_handler;
+
+ const uint32_t num_groups = gc_count_groups(c);
+
+ if (num_groups == 0) {
+ return data;
+ }
+
+ const uint32_t len = m_plugin_size(m, STATE_TYPE_GROUPS);
+
+ if (len == 0) {
+ return data;
+ }
+
+ data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_GROUPS);
+
+ if (!bin_pack_obj(pack_groupchats_handler, c, data, len)) {
+ LOGGER_FATAL(m->log, "failed to pack group chats into buffer of length %u", len);
+ return data;
+ }
+
+ data += len;
+
+ LOGGER_DEBUG(m->log, "Saved %u groups (length %u)", num_groups, len);
+
+ return data;
+}
+
+non_null()
+static State_Load_Status groups_load(Messenger *m, const uint8_t *data, uint32_t length)
+{
+ Bin_Unpack *bu = bin_unpack_new(data, length);
+ if (bu == nullptr) {
+ LOGGER_ERROR(m->log, "failed to allocate binary unpacker");
+ return STATE_LOAD_STATUS_ERROR;
+ }
+
+ uint32_t num_groups;
+ if (!bin_unpack_array(bu, &num_groups)) {
+ LOGGER_ERROR(m->log, "msgpack failed to unpack groupchats array: expected array");
+ bin_unpack_free(bu);
+ return STATE_LOAD_STATUS_ERROR;
+ }
+
+ LOGGER_DEBUG(m->log, "Loading %u groups (length %u)", num_groups, length);
+
+ for (uint32_t i = 0; i < num_groups; ++i) {
+ const int group_number = gc_group_load(m->group_handler, bu);
+
+ if (group_number < 0) {
+ LOGGER_WARNING(m->log, "Failed to load group %u", i);
+ }
+ }
+
+ bin_unpack_free(bu);
+
+ return STATE_LOAD_STATUS_CONTINUE;
+}
+#endif /* VANILLA_NACL */
+
// name state plugin
non_null()
static uint32_t name_size(const Messenger *m)
@@ -3072,6 +3413,9 @@ static void m_register_default_plugins(Messenger *m)
m_register_state_plugin(m, STATE_TYPE_STATUSMESSAGE, status_message_size, load_status_message,
save_status_message);
m_register_state_plugin(m, STATE_TYPE_STATUS, status_size, load_status, save_status);
+#ifndef VANILLA_NACL
+ m_register_state_plugin(m, STATE_TYPE_GROUPS, saved_groups_size, groups_load, groups_save);
+#endif
m_register_state_plugin(m, STATE_TYPE_TCP_RELAY, tcp_relay_size, load_tcp_relays, save_tcp_relays);
m_register_state_plugin(m, STATE_TYPE_PATH_NODE, path_node_size, load_path_nodes, save_path_nodes);
}
@@ -3244,6 +3588,21 @@ Messenger *new_messenger(Mono_Time *mono_time, const Random *rng, const Network
return nullptr;
}
+#ifndef VANILLA_NACL
+ m->group_announce = new_gca_list();
+
+ if (m->group_announce == nullptr) {
+ kill_net_crypto(m->net_crypto);
+ kill_dht(m->dht);
+ kill_networking(m->net);
+ friendreq_kill(m->fr);
+ logger_kill(m->log);
+ free(m);
+ return nullptr;
+ }
+
+#endif /* VANILLA_NACL */
+
if (options->dht_announcements_enabled) {
m->forwarding = new_forwarding(m->log, m->rng, m->mono_time, m->dht);
m->announce = new_announcements(m->log, m->rng, m->mono_time, m->forwarding);
@@ -3259,10 +3618,35 @@ Messenger *new_messenger(Mono_Time *mono_time, const Random *rng, const Network
if ((options->dht_announcements_enabled && (m->forwarding == nullptr || m->announce == nullptr)) ||
m->onion == nullptr || m->onion_a == nullptr || m->onion_c == nullptr || m->fr_c == nullptr) {
+ kill_onion(m->onion);
+ kill_onion_announce(m->onion_a);
+ kill_onion_client(m->onion_c);
+#ifndef VANILLA_NACL
+ kill_gca(m->group_announce);
+#endif /* VANILLA_NACL */
kill_friend_connections(m->fr_c);
+ kill_announcements(m->announce);
+ kill_forwarding(m->forwarding);
+ kill_net_crypto(m->net_crypto);
+ kill_dht(m->dht);
+ kill_networking(m->net);
+ friendreq_kill(m->fr);
+ logger_kill(m->log);
+ free(m);
+ return nullptr;
+ }
+
+#ifndef VANILLA_NACL
+ gca_onion_init(m->group_announce, m->onion_a);
+
+ m->group_handler = new_dht_groupchats(m);
+
+ if (m->group_handler == nullptr) {
kill_onion(m->onion);
kill_onion_announce(m->onion_a);
kill_onion_client(m->onion_c);
+ kill_gca(m->group_announce);
+ kill_friend_connections(m->fr_c);
kill_announcements(m->announce);
kill_forwarding(m->forwarding);
kill_net_crypto(m->net_crypto);
@@ -3274,15 +3658,23 @@ Messenger *new_messenger(Mono_Time *mono_time, const Random *rng, const Network
return nullptr;
}
+#endif /* VANILLA_NACL */
+
if (options->tcp_server_port != 0) {
m->tcp_server = new_TCP_server(m->log, m->rng, m->ns, options->ipv6enabled, 1, &options->tcp_server_port,
dht_get_self_secret_key(m->dht), m->onion, m->forwarding);
if (m->tcp_server == nullptr) {
- kill_friend_connections(m->fr_c);
kill_onion(m->onion);
kill_onion_announce(m->onion_a);
+#ifndef VANILLA_NACL
+ kill_dht_groupchats(m->group_handler);
+#endif
+ kill_friend_connections(m->fr_c);
kill_onion_client(m->onion_c);
+#ifndef VANILLA_NACL
+ kill_gca(m->group_announce);
+#endif
kill_announcements(m->announce);
kill_forwarding(m->forwarding);
kill_net_crypto(m->net_crypto);
@@ -3332,10 +3724,16 @@ void kill_messenger(Messenger *m)
kill_TCP_server(m->tcp_server);
}
- kill_friend_connections(m->fr_c);
kill_onion(m->onion);
kill_onion_announce(m->onion_a);
+#ifndef VANILLA_NACL
+ kill_dht_groupchats(m->group_handler);
+#endif
+ kill_friend_connections(m->fr_c);
kill_onion_client(m->onion_c);
+#ifndef VANILLA_NACL
+ kill_gca(m->group_announce);
+#endif
kill_announcements(m->announce);
kill_forwarding(m->forwarding);
kill_net_crypto(m->net_crypto);