mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Merge branch 'master' of https://github.com/irungentoo/toxcore
This commit is contained in:
commit
57666d95ba
|
@ -134,22 +134,12 @@ START_TEST(test_ip_equal)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_struct_sizes)
|
||||
{
|
||||
ck_assert_msg(sizeof(IP4) == 4, "sizeof(IP4): expected result 4, got %u.", sizeof(IP4));
|
||||
ck_assert_msg(sizeof(IP6) == 16, "sizeof(IP6): expected result 16, got %u.", sizeof(IP6));
|
||||
ck_assert_msg(sizeof(IP) == 17, "sizeof(IP): expected result 17, got %u.", sizeof(IP));
|
||||
ck_assert_msg(sizeof(IP_Port) == 19, "sizeof(IP_Port): expected result 19, got %u.", sizeof(IP_Port));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *network_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("Network");
|
||||
|
||||
DEFTESTCASE(addr_resolv_localhost);
|
||||
DEFTESTCASE(ip_equal);
|
||||
DEFTESTCASE(struct_sizes);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -342,7 +342,7 @@ Suite *onion_suite(void)
|
|||
Suite *s = suite_create("Onion");
|
||||
|
||||
DEFTESTCASE_SLOW(basic, 5);
|
||||
DEFTESTCASE_SLOW(announce, 50);
|
||||
//DEFTESTCASE_SLOW(announce, 50); //TODO: fix test.
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ complete API documentation is available in `tox.h`.
|
|||
|
||||
```
|
||||
#define TOX_AVATAR_MAX_DATA_LENGTH 16384
|
||||
#define TOX_AVATAR_HASH_LENGTH 32
|
||||
#define TOX_HASH_LENGTH 32
|
||||
|
||||
|
||||
/* Data formats for user avatar images */
|
||||
|
@ -146,8 +146,8 @@ int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t lengt
|
|||
/* Get avatar data from the current user. */
|
||||
int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash);
|
||||
|
||||
/* Generates a cryptographic hash of the given avatar data. */
|
||||
int tox_avatar_hash(const Tox *tox, uint8_t *hash, const uint8_t *data, const uint32_t datalen);
|
||||
/* Generates a cryptographic hash of the given data (usually a cached avatar). */
|
||||
int tox_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen);
|
||||
|
||||
/* Request avatar information from a friend. */
|
||||
int tox_request_avatar_info(const Tox *tox, const int32_t friendnumber);
|
||||
|
@ -337,7 +337,7 @@ As in this example:
|
|||
printf("Receiving avatar information from friend %d. Format = %d\n",
|
||||
friendnumber, format);
|
||||
printf("Data hash: ");
|
||||
hex_printf(hash, TOX_AVATAR_HASH_LENGTH); /* Hypothetical function */
|
||||
hex_printf(hash, TOX_HASH_LENGTH); /* Hypothetical function */
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
@ -597,11 +597,10 @@ The present proposal mitigates this situation by:
|
|||
avatar information when nothing has changed (`PACKET_ID_AVATAR_INFO`);
|
||||
|
||||
- Having per-friend data transfer limit. As the current protocol still
|
||||
allows an user to request an infinite data stream by asking the the
|
||||
same offset of the avatar again and again, the implementation limits
|
||||
the amount of data a single user can request for some time. For now,
|
||||
the library will not allow an user to request more than
|
||||
`10*TOX_AVATAR_MAX_DATA_LENGTH` in less than 20 minutes;
|
||||
allows an user to request avatar data again and again, the implementation
|
||||
limits the amount of data a particular user can request for some time. The
|
||||
exact values are defined in constants `AVATAR_DATA_TRANSFER_LIMIT` and
|
||||
`AVATAR_DATA_TRANSFER_TIMEOUT` in file `Messenger.c`.
|
||||
|
||||
- Making the requester responsible for storing partial data and state
|
||||
information;
|
||||
|
|
|
@ -208,7 +208,7 @@ int main(int argc, char *argv[])
|
|||
temp_id[strlen(temp_id) - 1] = '\0';
|
||||
|
||||
uint8_t *bin_id = hex_string_to_bin(temp_id);
|
||||
DHT_addfriend(dht, bin_id);
|
||||
DHT_addfriend(dht, bin_id, 0, 0, 0, 0);
|
||||
free(bin_id);
|
||||
|
||||
perror("Initialization");
|
||||
|
|
|
@ -1001,11 +1001,11 @@ void print_help(char *prog_name)
|
|||
puts(" -f keyfile [Optional] Specify a keyfile to read from and write to.");
|
||||
}
|
||||
|
||||
void print_invite(Tox *m, int friendnumber, const uint8_t *group_public_key, void *userdata)
|
||||
void print_invite(Tox *m, int friendnumber, const uint8_t *data, uint16_t length, void *userdata)
|
||||
{
|
||||
char msg[256];
|
||||
sprintf(msg, "[i] received group chat invite from: %u, auto accepting and joining. group number: %u", friendnumber,
|
||||
tox_join_groupchat(m, friendnumber, group_public_key));
|
||||
tox_join_groupchat(m, friendnumber, data, length));
|
||||
new_lines(msg);
|
||||
}
|
||||
|
||||
|
|
142
toxcore/DHT.c
142
toxcore/DHT.c
|
@ -620,7 +620,7 @@ static int replace_all( Client_data *list,
|
|||
const uint8_t *comp_client_id )
|
||||
{
|
||||
if ((ip_port.ip.family != AF_INET) && (ip_port.ip.family != AF_INET6))
|
||||
return 1;
|
||||
return 0;
|
||||
|
||||
uint32_t i, replace = ~0, bad = ~0, possibly_bad = ~0, good = ~0;
|
||||
|
||||
|
@ -708,14 +708,41 @@ int addto_lists(DHT *dht, IP_Port ip_port, const uint8_t *client_id)
|
|||
} else
|
||||
used++;
|
||||
|
||||
DHT_Friend *friend_foundip = 0;
|
||||
|
||||
for (i = 0; i < dht->num_friends; ++i) {
|
||||
if (!client_or_ip_port_in_list(dht->friends_list[i].client_list,
|
||||
MAX_FRIEND_CLIENTS, client_id, ip_port)) {
|
||||
if (replace_all(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS,
|
||||
client_id, ip_port, dht->friends_list[i].client_id))
|
||||
client_id, ip_port, dht->friends_list[i].client_id)) {
|
||||
|
||||
DHT_Friend *friend = &dht->friends_list[i];
|
||||
|
||||
if (memcmp(client_id, friend->client_id, CLIENT_ID_SIZE) == 0) {
|
||||
friend_foundip = friend;
|
||||
}
|
||||
|
||||
used++;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
DHT_Friend *friend = &dht->friends_list[i];
|
||||
|
||||
if (memcmp(client_id, friend->client_id, CLIENT_ID_SIZE) == 0) {
|
||||
friend_foundip = friend;
|
||||
}
|
||||
|
||||
used++;
|
||||
}
|
||||
}
|
||||
|
||||
if (friend_foundip) {
|
||||
uint32_t j;
|
||||
|
||||
for (j = 0; j < friend_foundip->lock_count; ++j) {
|
||||
if (friend_foundip->callbacks[j].ip_callback)
|
||||
friend_foundip->callbacks[j].ip_callback(friend_foundip->callbacks[j].data, friend_foundip->callbacks[j].number,
|
||||
ip_port);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_ASSOC_DHT
|
||||
|
@ -1089,23 +1116,54 @@ static void get_bunchnodes(DHT *dht, Client_data *list, uint16_t length, uint16_
|
|||
}
|
||||
}
|
||||
*/
|
||||
int DHT_addfriend(DHT *dht, const uint8_t *client_id)
|
||||
int DHT_addfriend(DHT *dht, const uint8_t *client_id, void (*ip_callback)(void *data, int32_t number, IP_Port),
|
||||
void *data, int32_t number, uint16_t *lock_count)
|
||||
{
|
||||
if (friend_number(dht, client_id) != -1) /* Is friend already in DHT? */
|
||||
return 1;
|
||||
int friend_num = friend_number(dht, client_id);
|
||||
|
||||
uint16_t lock_num;
|
||||
|
||||
if (friend_num != -1) { /* Is friend already in DHT? */
|
||||
DHT_Friend *friend = &dht->friends_list[friend_num];
|
||||
|
||||
if (friend->lock_count == DHT_FRIEND_MAX_LOCKS)
|
||||
return -1;
|
||||
|
||||
lock_num = friend->lock_count;
|
||||
++friend->lock_count;
|
||||
friend->callbacks[lock_num].ip_callback = ip_callback;
|
||||
friend->callbacks[lock_num].data = data;
|
||||
friend->callbacks[lock_num].number = number;
|
||||
|
||||
if (lock_count)
|
||||
*lock_count = lock_num + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DHT_Friend *temp;
|
||||
temp = realloc(dht->friends_list, sizeof(DHT_Friend) * (dht->num_friends + 1));
|
||||
|
||||
if (temp == NULL)
|
||||
return 1;
|
||||
return -1;
|
||||
|
||||
dht->friends_list = temp;
|
||||
memset(&dht->friends_list[dht->num_friends], 0, sizeof(DHT_Friend));
|
||||
memcpy(dht->friends_list[dht->num_friends].client_id, client_id, CLIENT_ID_SIZE);
|
||||
DHT_Friend *friend = &dht->friends_list[dht->num_friends];
|
||||
memset(friend, 0, sizeof(DHT_Friend));
|
||||
memcpy(friend->client_id, client_id, CLIENT_ID_SIZE);
|
||||
|
||||
dht->friends_list[dht->num_friends].nat.NATping_id = random_64b();
|
||||
friend->nat.NATping_id = random_64b();
|
||||
++dht->num_friends;
|
||||
|
||||
lock_num = friend->lock_count;
|
||||
++friend->lock_count;
|
||||
friend->callbacks[lock_num].ip_callback = ip_callback;
|
||||
friend->callbacks[lock_num].data = data;
|
||||
friend->callbacks[lock_num].number = number;
|
||||
|
||||
if (lock_count)
|
||||
*lock_count = lock_num + 1;
|
||||
|
||||
#ifdef ENABLE_ASSOC_DHT
|
||||
|
||||
if (dht->assoc) {
|
||||
|
@ -1143,39 +1201,49 @@ int DHT_addfriend(DHT *dht, const uint8_t *client_id)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int DHT_delfriend(DHT *dht, const uint8_t *client_id)
|
||||
int DHT_delfriend(DHT *dht, const uint8_t *client_id, uint16_t lock_count)
|
||||
{
|
||||
int friend_num = friend_number(dht, client_id);
|
||||
|
||||
if (friend_num == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DHT_Friend *friend = &dht->friends_list[friend_num];
|
||||
--friend->lock_count;
|
||||
|
||||
if (friend->lock_count && lock_count) { /* DHT friend is still in use.*/
|
||||
--lock_count;
|
||||
friend->callbacks[lock_count].ip_callback = NULL;
|
||||
friend->callbacks[lock_count].data = NULL;
|
||||
friend->callbacks[lock_count].number = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t i;
|
||||
DHT_Friend *temp;
|
||||
|
||||
for (i = 0; i < dht->num_friends; ++i) {
|
||||
/* Equal */
|
||||
if (id_equal(dht->friends_list[i].client_id, client_id)) {
|
||||
--dht->num_friends;
|
||||
--dht->num_friends;
|
||||
|
||||
if (dht->num_friends != i) {
|
||||
memcpy( &dht->friends_list[i],
|
||||
&dht->friends_list[dht->num_friends],
|
||||
sizeof(DHT_Friend) );
|
||||
}
|
||||
|
||||
if (dht->num_friends == 0) {
|
||||
free(dht->friends_list);
|
||||
dht->friends_list = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
temp = realloc(dht->friends_list, sizeof(DHT_Friend) * (dht->num_friends));
|
||||
|
||||
if (temp == NULL)
|
||||
return 1;
|
||||
|
||||
dht->friends_list = temp;
|
||||
return 0;
|
||||
}
|
||||
if (dht->num_friends != friend_num) {
|
||||
memcpy( &dht->friends_list[friend_num],
|
||||
&dht->friends_list[dht->num_friends],
|
||||
sizeof(DHT_Friend) );
|
||||
}
|
||||
|
||||
return 1;
|
||||
if (dht->num_friends == 0) {
|
||||
free(dht->friends_list);
|
||||
dht->friends_list = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
temp = realloc(dht->friends_list, sizeof(DHT_Friend) * (dht->num_friends));
|
||||
|
||||
if (temp == NULL)
|
||||
return -1;
|
||||
|
||||
dht->friends_list = temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: Optimize this. */
|
||||
|
@ -2216,7 +2284,7 @@ DHT *new_DHT(Networking_Core *net)
|
|||
for (i = 0; i < DHT_FAKE_FRIEND_NUMBER; ++i) {
|
||||
uint8_t random_key_bytes[CLIENT_ID_SIZE];
|
||||
randombytes(random_key_bytes, sizeof(random_key_bytes));
|
||||
DHT_addfriend(dht, random_key_bytes);
|
||||
DHT_addfriend(dht, random_key_bytes, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
return dht;
|
||||
|
|
|
@ -122,6 +122,8 @@ typedef struct {
|
|||
uint64_t NATping_timestamp;
|
||||
} NAT;
|
||||
|
||||
#define DHT_FRIEND_MAX_LOCKS 32
|
||||
|
||||
typedef struct {
|
||||
uint8_t client_id[CLIENT_ID_SIZE];
|
||||
Client_data client_list[MAX_FRIEND_CLIENTS];
|
||||
|
@ -133,10 +135,17 @@ typedef struct {
|
|||
|
||||
/* Symetric NAT hole punching stuff. */
|
||||
NAT nat;
|
||||
|
||||
uint16_t lock_count;
|
||||
struct {
|
||||
void (*ip_callback)(void *, int32_t, IP_Port);
|
||||
void *data;
|
||||
int32_t number;
|
||||
} callbacks[DHT_FRIEND_MAX_LOCKS];
|
||||
|
||||
} DHT_Friend;
|
||||
|
||||
typedef struct __attribute__ ((__packed__))
|
||||
{
|
||||
typedef struct {
|
||||
uint8_t client_id[CLIENT_ID_SIZE];
|
||||
IP_Port ip_port;
|
||||
}
|
||||
|
@ -246,18 +255,25 @@ void DHT_getnodes(DHT *dht, const IP_Port *from_ipp, const uint8_t *from_id, con
|
|||
/* Add a new friend to the friends list.
|
||||
* client_id must be CLIENT_ID_SIZE bytes long.
|
||||
*
|
||||
* ip_callback is the callback of a function that will be called when the ip address
|
||||
* is found along with arguments data and number.
|
||||
*
|
||||
* lock_count will be set to a non zero number that must be passed to DHT_delfriend()
|
||||
* to properly remove the callback.
|
||||
*
|
||||
* return 0 if success.
|
||||
* return 1 if failure (friends list is full).
|
||||
* return -1 if failure (friends list is full).
|
||||
*/
|
||||
int DHT_addfriend(DHT *dht, const uint8_t *client_id);
|
||||
int DHT_addfriend(DHT *dht, const uint8_t *client_id, void (*ip_callback)(void *data, int32_t number, IP_Port),
|
||||
void *data, int32_t number, uint16_t *lock_count);
|
||||
|
||||
/* Delete a friend from the friends list.
|
||||
* client_id must be CLIENT_ID_SIZE bytes long.
|
||||
*
|
||||
* return 0 if success.
|
||||
* return 1 if failure (client_id not in friends list).
|
||||
* return -1 if failure (client_id not in friends list).
|
||||
*/
|
||||
int DHT_delfriend(DHT *dht, const uint8_t *client_id);
|
||||
int DHT_delfriend(DHT *dht, const uint8_t *client_id, uint16_t lock_count);
|
||||
|
||||
/* Get ip of friend.
|
||||
* client_id must be CLIENT_ID_SIZE bytes long.
|
||||
|
|
|
@ -19,6 +19,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \
|
|||
../toxcore/friend_requests.c \
|
||||
../toxcore/LAN_discovery.h \
|
||||
../toxcore/LAN_discovery.c \
|
||||
../toxcore/friend_connection.h \
|
||||
../toxcore/friend_connection.c \
|
||||
../toxcore/Messenger.h \
|
||||
../toxcore/Messenger.c \
|
||||
../toxcore/ping.h \
|
||||
|
@ -27,8 +29,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \
|
|||
../toxcore/tox.c \
|
||||
../toxcore/util.h \
|
||||
../toxcore/util.c \
|
||||
../toxcore/group_chats.h \
|
||||
../toxcore/group_chats.c \
|
||||
../toxcore/group.h \
|
||||
../toxcore/group.c \
|
||||
../toxcore/assoc.h \
|
||||
../toxcore/assoc.c \
|
||||
../toxcore/onion.h \
|
||||
|
|
|
@ -153,20 +153,9 @@ void getaddress(const Messenger *m, uint8_t *address)
|
|||
memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(nospam), &checksum, sizeof(checksum));
|
||||
}
|
||||
|
||||
/* callback for recv TCP relay nodes. */
|
||||
static int tcp_relay_node_callback(void *object, uint32_t number, IP_Port ip_port, const uint8_t *public_key)
|
||||
{
|
||||
Messenger *m = object;
|
||||
|
||||
if (friend_not_valid(m, number))
|
||||
return -1;
|
||||
|
||||
if (m->friendlist[number].crypt_connection_id != -1) {
|
||||
return add_tcp_relay_peer(m->net_crypto, m->friendlist[number].crypt_connection_id, ip_port, public_key);
|
||||
} else {
|
||||
return add_tcp_relay(m->net_crypto, ip_port, public_key);
|
||||
}
|
||||
}
|
||||
static int handle_status(void *object, int i, uint8_t status);
|
||||
static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len);
|
||||
static int handle_custom_lossy_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length);
|
||||
|
||||
/*
|
||||
* Add a friend.
|
||||
|
@ -230,18 +219,17 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u
|
|||
|
||||
memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend));
|
||||
|
||||
int32_t onion_friendnum = onion_addfriend(m->onion_c, client_id);
|
||||
int friendcon_id = new_friend_connection(m->fr_c, client_id);
|
||||
|
||||
if (onion_friendnum == -1)
|
||||
return FAERR_UNKNOWN;
|
||||
if (friendcon_id == -1)
|
||||
return -1;
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i <= m->numfriends; ++i) {
|
||||
if (m->friendlist[i].status == NOFRIEND) {
|
||||
m->friendlist[i].onion_friendnum = onion_friendnum;
|
||||
m->friendlist[i].status = FRIEND_ADDED;
|
||||
m->friendlist[i].crypt_connection_id = -1;
|
||||
m->friendlist[i].friendcon_id = friendcon_id;
|
||||
m->friendlist[i].friendrequest_lastsent = 0;
|
||||
m->friendlist[i].friendrequest_timeout = FRIENDREQUEST_TIMEOUT;
|
||||
id_copy(m->friendlist[i].client_id, client_id);
|
||||
|
@ -258,7 +246,9 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u
|
|||
m->friendlist[i].message_id = 0;
|
||||
m->friendlist[i].receives_read_receipts = 1; /* Default: YES. */
|
||||
memcpy(&(m->friendlist[i].friendrequest_nospam), address + crypto_box_PUBLICKEYBYTES, sizeof(uint32_t));
|
||||
recv_tcp_relay_handler(m->onion_c, onion_friendnum, &tcp_relay_node_callback, m, i);
|
||||
friend_connection_callbacks(m->fr_c, friendcon_id, MESSENGER_CALLBACK_INDEX, &handle_status, &handle_packet,
|
||||
&handle_custom_lossy_packet, m, i);
|
||||
|
||||
|
||||
if (m->numfriends == i)
|
||||
++m->numfriends;
|
||||
|
@ -287,18 +277,17 @@ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *client_id)
|
|||
|
||||
memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend));
|
||||
|
||||
int32_t onion_friendnum = onion_addfriend(m->onion_c, client_id);
|
||||
int friendcon_id = new_friend_connection(m->fr_c, client_id);
|
||||
|
||||
if (onion_friendnum == -1)
|
||||
if (friendcon_id == -1)
|
||||
return -1;
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i <= m->numfriends; ++i) {
|
||||
if (m->friendlist[i].status == NOFRIEND) {
|
||||
m->friendlist[i].onion_friendnum = onion_friendnum;
|
||||
m->friendlist[i].status = FRIEND_CONFIRMED;
|
||||
m->friendlist[i].crypt_connection_id = -1;
|
||||
m->friendlist[i].friendcon_id = friendcon_id;
|
||||
m->friendlist[i].friendrequest_lastsent = 0;
|
||||
id_copy(m->friendlist[i].client_id, client_id);
|
||||
m->friendlist[i].statusmessage = calloc(1, 1);
|
||||
|
@ -311,7 +300,8 @@ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *client_id)
|
|||
m->friendlist[i].is_typing = 0;
|
||||
m->friendlist[i].message_id = 0;
|
||||
m->friendlist[i].receives_read_receipts = 1; /* Default: YES. */
|
||||
recv_tcp_relay_handler(m->onion_c, onion_friendnum, &tcp_relay_node_callback, m, i);
|
||||
friend_connection_callbacks(m->fr_c, friendcon_id, MESSENGER_CALLBACK_INDEX, &handle_status, &handle_packet,
|
||||
&handle_custom_lossy_packet, m, i);
|
||||
|
||||
if (m->numfriends == i)
|
||||
++m->numfriends;
|
||||
|
@ -336,11 +326,11 @@ int m_delfriend(Messenger *m, int32_t friendnumber)
|
|||
if (m->friendlist[friendnumber].status == FRIEND_ONLINE)
|
||||
remove_online_friend(m, friendnumber);
|
||||
|
||||
onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum);
|
||||
crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id);
|
||||
free(m->friendlist[friendnumber].statusmessage);
|
||||
free(m->friendlist[friendnumber].avatar_recv_data);
|
||||
remove_request_received(&(m->fr), m->friendlist[friendnumber].client_id);
|
||||
friend_connection_callbacks(m->fr_c, m->friendlist[friendnumber].friendcon_id, MESSENGER_CALLBACK_INDEX, 0, 0, 0, 0, 0);
|
||||
kill_friend_connection(m->fr_c, m->friendlist[friendnumber].friendcon_id);
|
||||
memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend));
|
||||
uint32_t i;
|
||||
|
||||
|
@ -492,10 +482,6 @@ int setname(Messenger *m, const uint8_t *name, uint16_t length)
|
|||
for (i = 0; i < m->numfriends; ++i)
|
||||
m->friendlist[i].name_sent = 0;
|
||||
|
||||
for (i = 0; i < m->numchats; i++)
|
||||
if (m->chats[i] != NULL)
|
||||
set_nick(m->chats[i], name, length); /* TODO: remove this (group nicks should not be tied to the global one) */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -574,33 +560,48 @@ int m_set_userstatus(Messenger *m, uint8_t status)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int m_unset_avatar(Messenger *m)
|
||||
{
|
||||
if (m->avatar_data != NULL)
|
||||
free(m->avatar_data);
|
||||
|
||||
m->avatar_data = NULL;
|
||||
m->avatar_data_length = 0;
|
||||
m->avatar_format = AVATAR_FORMAT_NONE;
|
||||
memset(m->avatar_hash, 0, AVATAR_HASH_LENGTH);
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < m->numfriends; ++i)
|
||||
m->friendlist[i].avatar_info_sent = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int m_set_avatar(Messenger *m, uint8_t format, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
if (length > AVATAR_MAX_DATA_LENGTH)
|
||||
if (format == AVATAR_FORMAT_NONE) {
|
||||
m_unset_avatar(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (length > AVATAR_MAX_DATA_LENGTH || length == 0)
|
||||
return -1;
|
||||
|
||||
if (format == AVATAR_FORMAT_NONE) {
|
||||
free(m->avatar_data);
|
||||
m->avatar_data = NULL;
|
||||
m->avatar_data_length = 0;
|
||||
m->avatar_format = format;
|
||||
memset(m->avatar_hash, 0, AVATAR_HASH_LENGTH);
|
||||
} else {
|
||||
if (length == 0 || data == NULL)
|
||||
return -1;
|
||||
if (data == NULL)
|
||||
return -1;
|
||||
|
||||
uint8_t *tmp = realloc(m->avatar_data, length);
|
||||
uint8_t *tmp = realloc(m->avatar_data, length);
|
||||
|
||||
if (tmp == NULL)
|
||||
return -1;
|
||||
if (tmp == NULL)
|
||||
return -1;
|
||||
|
||||
m->avatar_format = format;
|
||||
m->avatar_data = tmp;
|
||||
m->avatar_data_length = length;
|
||||
memcpy(m->avatar_data, data, length);
|
||||
m->avatar_format = format;
|
||||
m->avatar_data = tmp;
|
||||
m->avatar_data_length = length;
|
||||
memcpy(m->avatar_data, data, length);
|
||||
|
||||
m_avatar_hash(m->avatar_hash, m->avatar_data, m->avatar_data_length);
|
||||
}
|
||||
m_avatar_hash(m->avatar_hash, m->avatar_data, m->avatar_data_length);
|
||||
|
||||
uint32_t i;
|
||||
|
||||
|
@ -811,16 +812,6 @@ static int send_user_istyping(const Messenger *m, int32_t friendnumber, uint8_t
|
|||
return write_cryptpacket_id(m, friendnumber, PACKET_ID_TYPING, &typing, sizeof(typing), 0);
|
||||
}
|
||||
|
||||
static int send_ping(const Messenger *m, int32_t friendnumber)
|
||||
{
|
||||
int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_ALIVE, 0, 0, 0);
|
||||
|
||||
if (ret == 1)
|
||||
m->friendlist[friendnumber].ping_lastsent = unix_time();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int send_relays(const Messenger *m, int32_t friendnumber)
|
||||
{
|
||||
Node_format nodes[MAX_SHARED_RELAYS];
|
||||
|
@ -967,8 +958,6 @@ static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, ui
|
|||
const uint8_t was_online = m->friendlist[friendnumber].status == FRIEND_ONLINE;
|
||||
const uint8_t is_online = status == FRIEND_ONLINE;
|
||||
|
||||
onion_set_friend_online(m->onion_c, m->friendlist[friendnumber].onion_friendnum, is_online);
|
||||
|
||||
if (is_online != was_online) {
|
||||
if (was_online) {
|
||||
break_files(m, friendnumber);
|
||||
|
@ -1009,464 +998,31 @@ static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_
|
|||
if (length != 0)
|
||||
memcpy(packet + 1, data, length);
|
||||
|
||||
return write_cryptpacket(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id, packet, length + 1,
|
||||
congestion_control) != -1;
|
||||
return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
|
||||
m->friendlist[friendnumber].friendcon_id), packet, length + 1, congestion_control) != -1;
|
||||
}
|
||||
|
||||
/**********GROUP CHATS************/
|
||||
|
||||
/* return 1 if the groupnumber is not valid.
|
||||
* return 0 if the groupnumber is valid.
|
||||
*/
|
||||
static uint8_t groupnumber_not_valid(const Messenger *m, int groupnumber)
|
||||
{
|
||||
if ((unsigned int)groupnumber >= m->numchats)
|
||||
return 1;
|
||||
|
||||
if (m->chats == NULL)
|
||||
return 1;
|
||||
|
||||
if (m->chats[groupnumber] == NULL)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* returns valid ip port of connected friend on success
|
||||
* returns zeroed out IP_Port on failure
|
||||
*/
|
||||
static IP_Port get_friend_ipport(const Messenger *m, int32_t friendnumber)
|
||||
{
|
||||
IP_Port zero;
|
||||
memset(&zero, 0, sizeof(zero));
|
||||
|
||||
if (friend_not_valid(m, friendnumber))
|
||||
return zero;
|
||||
|
||||
int crypt_id = m->friendlist[friendnumber].crypt_connection_id;
|
||||
|
||||
uint8_t direct_connected;
|
||||
|
||||
if (crypto_connection_status(m->net_crypto, crypt_id, &direct_connected) != CRYPTO_CONN_ESTABLISHED)
|
||||
return zero;
|
||||
|
||||
if (direct_connected == 0)
|
||||
return zero;
|
||||
|
||||
return m->net_crypto->crypto_connections[crypt_id].ip_port;
|
||||
}
|
||||
|
||||
/* returns the group number of the chat with public key group_public_key.
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
static int group_num(const Messenger *m, const uint8_t *group_public_key)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < m->numchats; ++i) {
|
||||
if (m->chats[i] != NULL)
|
||||
if (id_equal(m->chats[i]->self_public_key, group_public_key))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the callback for group invites.
|
||||
*
|
||||
* Function(Messenger *m, int32_t friendnumber, uint8_t *group_public_key, void *userdata)
|
||||
* Function(Messenger *m, int32_t friendnumber, uint8_t *data, uint16_t length)
|
||||
*/
|
||||
void m_callback_group_invite(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, void *),
|
||||
void *userdata)
|
||||
void m_callback_group_invite(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t))
|
||||
{
|
||||
m->group_invite = function;
|
||||
m->group_invite_userdata = userdata;
|
||||
}
|
||||
|
||||
/* Set the callback for group messages.
|
||||
|
||||
/* Send a group invite packet.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata)
|
||||
* return 1 on success
|
||||
* return 0 on failure
|
||||
*/
|
||||
void m_callback_group_message(Messenger *m, void (*function)(Messenger *m, int, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata)
|
||||
int send_group_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
m->group_message = function;
|
||||
m->group_message_userdata = userdata;
|
||||
}
|
||||
|
||||
/* Set the callback for group actions.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata)
|
||||
*/
|
||||
void m_callback_group_action(Messenger *m, void (*function)(Messenger *m, int, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata)
|
||||
{
|
||||
m->group_action = function;
|
||||
m->group_action_userdata = userdata;
|
||||
}
|
||||
|
||||
/* Set callback function for peer name list changes.
|
||||
*
|
||||
* It gets called every time the name list changes(new peer/name, deleted peer)
|
||||
* Function(Tox *tox, int groupnumber, void *userdata)
|
||||
*/
|
||||
void m_callback_group_namelistchange(Messenger *m, void (*function)(Messenger *m, int, int, uint8_t, void *),
|
||||
void *userdata)
|
||||
{
|
||||
m->group_namelistchange = function;
|
||||
m->group_namelistchange_userdata = userdata;
|
||||
}
|
||||
|
||||
static int get_chat_num(const Messenger *m, const Group_Chat *chat)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < m->numchats; ++i) { //TODO: remove this
|
||||
if (m->chats[i] == chat)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void group_message_function(Group_Chat *chat, int peer_number, const uint8_t *message, uint16_t length,
|
||||
void *userdata)
|
||||
{
|
||||
Messenger *m = userdata;
|
||||
int i = get_chat_num(m, chat);
|
||||
|
||||
if (i == -1)
|
||||
return;
|
||||
|
||||
uint8_t message_terminated[length + 1];
|
||||
memcpy(message_terminated, message, length);
|
||||
message_terminated[length] = 0; /* Force NULL terminator */
|
||||
|
||||
if (m->group_message)
|
||||
(*m->group_message)(m, i, peer_number, message_terminated, length, m->group_message_userdata);
|
||||
}
|
||||
|
||||
static void group_action_function(Group_Chat *chat, int peer_number, const uint8_t *action, uint16_t length,
|
||||
void *userdata)
|
||||
{
|
||||
Messenger *m = userdata;
|
||||
int i = get_chat_num(m, chat);
|
||||
|
||||
if (i == -1)
|
||||
return;
|
||||
|
||||
uint8_t action_terminated[length + 1];
|
||||
memcpy(action_terminated, action, length);
|
||||
action_terminated[length] = 0; /* Force NULL terminator */
|
||||
|
||||
if (m->group_action)
|
||||
(*m->group_action)(m, i, peer_number, action_terminated, length, m->group_action_userdata);
|
||||
}
|
||||
|
||||
static void group_namelistchange_function(Group_Chat *chat, int peer, uint8_t change, void *userdata)
|
||||
{
|
||||
Messenger *m = userdata;
|
||||
int i = get_chat_num(m, chat);
|
||||
|
||||
if (i == -1)
|
||||
return;
|
||||
|
||||
if (m->group_namelistchange)
|
||||
(*m->group_namelistchange)(m, i, peer, change, m->group_namelistchange_userdata);
|
||||
}
|
||||
|
||||
|
||||
/* Creates a new groupchat and puts it in the chats array.
|
||||
*
|
||||
* return group number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_groupchat(Messenger *m)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < m->numchats; ++i) {
|
||||
if (m->chats[i] == NULL) {
|
||||
Group_Chat *newchat = new_groupchat(m->net);
|
||||
|
||||
if (newchat == NULL)
|
||||
return -1;
|
||||
|
||||
callback_groupmessage(newchat, &group_message_function, m);
|
||||
callback_groupaction(newchat, &group_action_function, m);
|
||||
callback_namelistchange(newchat, &group_namelistchange_function, m);
|
||||
/* TODO: remove this (group nicks should not be tied to the global one) */
|
||||
set_nick(newchat, m->name, m->name_length);
|
||||
m->chats[i] = newchat;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
Group_Chat **temp;
|
||||
temp = realloc(m->chats, sizeof(Group_Chat *) * (m->numchats + 1));
|
||||
|
||||
if (temp == NULL)
|
||||
return -1;
|
||||
|
||||
m->chats = temp;
|
||||
temp[m->numchats] = new_groupchat(m->net);
|
||||
|
||||
if (temp[m->numchats] == NULL)
|
||||
return -1;
|
||||
|
||||
callback_groupmessage(temp[m->numchats], &group_message_function, m);
|
||||
callback_groupaction(temp[m->numchats], &group_action_function, m);
|
||||
callback_namelistchange(temp[m->numchats], &group_namelistchange_function, m);
|
||||
/* TODO: remove this (group nicks should not be tied to the global one) */
|
||||
set_nick(temp[m->numchats], m->name, m->name_length);
|
||||
++m->numchats;
|
||||
return (m->numchats - 1);
|
||||
}
|
||||
|
||||
/* Delete a groupchat from the chats array.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 if failure.
|
||||
*/
|
||||
int del_groupchat(Messenger *m, int groupnumber)
|
||||
{
|
||||
if ((unsigned int)groupnumber >= m->numchats)
|
||||
return -1;
|
||||
|
||||
if (m->chats == NULL)
|
||||
return -1;
|
||||
|
||||
if (m->chats[groupnumber] == NULL)
|
||||
return -1;
|
||||
|
||||
kill_groupchat(m->chats[groupnumber]);
|
||||
m->chats[groupnumber] = NULL;
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = m->numchats; i != 0; --i) {
|
||||
if (m->chats[i - 1] != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
m->numchats = i;
|
||||
|
||||
if (i == 0) {
|
||||
free(m->chats);
|
||||
m->chats = NULL;
|
||||
} else {
|
||||
Group_Chat **temp = realloc(m->chats, sizeof(Group_Chat *) * i);
|
||||
|
||||
if (temp != NULL)
|
||||
m->chats = temp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy the name of peernumber who is in groupnumber to name.
|
||||
* name must be at least MAX_NICK_BYTES long.
|
||||
*
|
||||
* return length of name if success
|
||||
* return -1 if failure
|
||||
*/
|
||||
int m_group_peername(const Messenger *m, int groupnumber, int peernumber, uint8_t *name)
|
||||
{
|
||||
if ((unsigned int)groupnumber >= m->numchats)
|
||||
return -1;
|
||||
|
||||
if (m->chats == NULL)
|
||||
return -1;
|
||||
|
||||
if (m->chats[groupnumber] == NULL)
|
||||
return -1;
|
||||
|
||||
return group_peername(m->chats[groupnumber], peernumber, name);
|
||||
}
|
||||
|
||||
/* Store the fact that we invited a specific friend.
|
||||
*/
|
||||
static void group_store_friendinvite(Messenger *m, int32_t friendnumber, int groupnumber)
|
||||
{
|
||||
/* Add 1 to the groupchat number because 0 (default value in invited_groups) is a valid groupchat number */
|
||||
m->friendlist[friendnumber].invited_groups[m->friendlist[friendnumber].invited_groups_num % MAX_INVITED_GROUPS] =
|
||||
groupnumber + 1;
|
||||
++m->friendlist[friendnumber].invited_groups_num;
|
||||
}
|
||||
|
||||
/* return 1 if that friend was invited to the group
|
||||
* return 0 if the friend was not or error.
|
||||
*/
|
||||
static uint8_t group_invited(const Messenger *m, int32_t friendnumber, int groupnumber)
|
||||
{
|
||||
|
||||
uint32_t i;
|
||||
uint16_t num = MAX_INVITED_GROUPS;
|
||||
|
||||
if (MAX_INVITED_GROUPS > m->friendlist[friendnumber].invited_groups_num)
|
||||
num = m->friendlist[friendnumber].invited_groups_num;
|
||||
|
||||
for (i = 0; i < num; ++i) {
|
||||
if (m->friendlist[friendnumber].invited_groups[i] == groupnumber + 1) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* invite friendnumber to groupnumber
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int invite_friend(Messenger *m, int32_t friendnumber, int groupnumber)
|
||||
{
|
||||
if (friend_not_valid(m, friendnumber) || (unsigned int)groupnumber >= m->numchats)
|
||||
return -1;
|
||||
|
||||
if (m->chats == NULL)
|
||||
return -1;
|
||||
|
||||
if (m->friendlist[friendnumber].status == NOFRIEND || m->chats[groupnumber] == NULL)
|
||||
return -1;
|
||||
|
||||
group_store_friendinvite(m, friendnumber, groupnumber);
|
||||
|
||||
if (write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_GROUPCHAT, m->chats[groupnumber]->self_public_key,
|
||||
crypto_box_PUBLICKEYBYTES, 0) == 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Join a group (you need to have been invited first.)
|
||||
*
|
||||
* returns group number on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int join_groupchat(Messenger *m, int32_t friendnumber, const uint8_t *friend_group_public_key)
|
||||
{
|
||||
if (friend_not_valid(m, friendnumber))
|
||||
return -1;
|
||||
|
||||
uint8_t data[crypto_box_PUBLICKEYBYTES * 2];
|
||||
int groupnum = add_groupchat(m);
|
||||
|
||||
if (groupnum == -1)
|
||||
return -1;
|
||||
|
||||
IP_Port friend_ip = get_friend_ipport(m, friendnumber);
|
||||
|
||||
if (friend_ip.ip.family == 0) {
|
||||
del_groupchat(m, groupnum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
id_copy(data, friend_group_public_key);
|
||||
id_copy(data + crypto_box_PUBLICKEYBYTES, m->chats[groupnum]->self_public_key);
|
||||
|
||||
if (write_cryptpacket_id(m, friendnumber, PACKET_ID_JOIN_GROUPCHAT, data, sizeof(data), 0)) {
|
||||
chat_bootstrap_nonlazy(m->chats[groupnum], get_friend_ipport(m, friendnumber),
|
||||
friend_group_public_key); //TODO: check if ip returned is zero?
|
||||
return groupnum;
|
||||
}
|
||||
|
||||
del_groupchat(m, groupnum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* send a group message
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_message_send(const Messenger *m, int groupnumber, const uint8_t *message, uint32_t length)
|
||||
{
|
||||
if (groupnumber_not_valid(m, groupnumber))
|
||||
return -1;
|
||||
|
||||
if (group_sendmessage(m->chats[groupnumber], message, length) > 0)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* send a group action
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_action_send(const Messenger *m, int groupnumber, const uint8_t *action, uint32_t length)
|
||||
{
|
||||
if (groupnumber_not_valid(m, groupnumber))
|
||||
return -1;
|
||||
|
||||
if (group_sendaction(m->chats[groupnumber], action, length) > 0)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the number of peers in the group chat on success.
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_number_peers(const Messenger *m, int groupnumber)
|
||||
{
|
||||
if (groupnumber_not_valid(m, groupnumber))
|
||||
return -1;
|
||||
|
||||
return group_numpeers(m->chats[groupnumber]);
|
||||
}
|
||||
|
||||
/* List all the peers in the group chat.
|
||||
*
|
||||
* Copies the names of the peers to the name[length][MAX_NICK_BYTES] array.
|
||||
*
|
||||
* Copies the lengths of the names to lengths[length]
|
||||
*
|
||||
* returns the number of peers on success.
|
||||
*
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int group_names(const Messenger *m, int groupnumber, uint8_t names[][MAX_NICK_BYTES], uint16_t lengths[],
|
||||
uint16_t length)
|
||||
{
|
||||
if (groupnumber_not_valid(m, groupnumber))
|
||||
return -1;
|
||||
|
||||
return group_client_names(m->chats[groupnumber], names, lengths, length);
|
||||
}
|
||||
|
||||
static int handle_group(void *object, IP_Port source, const uint8_t *packet, uint32_t length)
|
||||
{
|
||||
Messenger *m = object;
|
||||
|
||||
if (length < crypto_box_PUBLICKEYBYTES + 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < m->numchats; ++i) {
|
||||
if (m->chats[i] == NULL)
|
||||
continue;
|
||||
|
||||
if (id_equal(packet + 1, m->chats[i]->self_public_key))
|
||||
return handle_groupchatpacket(m->chats[i], source, packet, length);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void do_allgroupchats(Messenger *m)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < m->numchats; ++i) {
|
||||
if (m->chats[i] != NULL)
|
||||
do_groupchat(m->chats[i]);
|
||||
}
|
||||
return write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_GROUPCHAT, data, length, 0);
|
||||
}
|
||||
|
||||
/****************FILE SENDING*****************/
|
||||
|
@ -1669,8 +1225,9 @@ int file_data(const Messenger *m, int32_t friendnumber, uint8_t filenumber, cons
|
|||
if (m->friendlist[friendnumber].file_sending[filenumber].status != FILESTATUS_TRANSFERRING)
|
||||
return -1;
|
||||
|
||||
/* Prevent file sending from filling up the entire buffer preventing messages from being sent. */
|
||||
if (crypto_num_free_sendqueue_slots(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id) < MIN_SLOTS_FREE)
|
||||
/* Prevent file sending from filling up the entire buffer preventing messages from being sent. TODO: remove */
|
||||
if (crypto_num_free_sendqueue_slots(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
|
||||
m->friendlist[friendnumber].friendcon_id)) < MIN_SLOTS_FREE)
|
||||
return -1;
|
||||
|
||||
uint8_t packet[MAX_CRYPTO_DATA_SIZE];
|
||||
|
@ -1876,10 +1433,8 @@ int send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uin
|
|||
if (m->friendlist[friendnumber].status != FRIEND_ONLINE)
|
||||
return -1;
|
||||
|
||||
if (m->friendlist[friendnumber].crypt_connection_id == -1)
|
||||
return -1;
|
||||
|
||||
return send_lossy_cryptpacket(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id, data, length);
|
||||
return send_lossy_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
|
||||
m->friendlist[friendnumber].friendcon_id), data, length);
|
||||
}
|
||||
|
||||
static int handle_custom_lossless_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length)
|
||||
|
@ -1937,10 +1492,8 @@ int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const
|
|||
if (m->friendlist[friendnumber].status != FRIEND_ONLINE)
|
||||
return -1;
|
||||
|
||||
if (m->friendlist[friendnumber].crypt_connection_id == -1)
|
||||
return -1;
|
||||
|
||||
if (write_cryptpacket(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id, data, length, 1) == -1) {
|
||||
if (write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
|
||||
m->friendlist[friendnumber].friendcon_id), data, length, 1) == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
|
@ -1967,31 +1520,6 @@ static void LANdiscovery(Messenger *m)
|
|||
}
|
||||
}
|
||||
|
||||
static int handle_status(void *object, int i, uint8_t status);
|
||||
static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len);
|
||||
|
||||
static int handle_new_connections(void *object, New_Connection *n_c)
|
||||
{
|
||||
Messenger *m = object;
|
||||
int friend_id = getfriend_id(m, n_c->public_key);
|
||||
|
||||
if (friend_id != -1) {
|
||||
if (m->friendlist[friend_id].crypt_connection_id != -1)
|
||||
return -1;
|
||||
|
||||
int id = accept_crypto_connection(m->net_crypto, n_c);
|
||||
connection_status_handler(m->net_crypto, id, &handle_status, m, friend_id);
|
||||
connection_data_handler(m->net_crypto, id, &handle_packet, m, friend_id);
|
||||
connection_lossy_data_handler(m->net_crypto, id, &handle_custom_lossy_packet, m, friend_id);
|
||||
m->friendlist[friend_id].crypt_connection_id = id;
|
||||
set_friend_status(m, friend_id, FRIEND_CONFIRMED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Run this at startup. */
|
||||
Messenger *new_messenger(Messenger_Options *options)
|
||||
{
|
||||
|
@ -2038,13 +1566,13 @@ Messenger *new_messenger(Messenger_Options *options)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
new_connection_handler(m->net_crypto, &handle_new_connections, m);
|
||||
|
||||
m->onion = new_onion(m->dht);
|
||||
m->onion_a = new_onion_announce(m->dht);
|
||||
m->onion_c = new_onion_client(m->net_crypto);
|
||||
m->fr_c = new_friend_connections(m->onion_c);
|
||||
|
||||
if (!(m->onion && m->onion_a && m->onion_c)) {
|
||||
kill_friend_connections(m->fr_c);
|
||||
kill_onion(m->onion);
|
||||
kill_onion_announce(m->onion_a);
|
||||
kill_onion_client(m->onion_c);
|
||||
|
@ -2061,22 +1589,15 @@ Messenger *new_messenger(Messenger_Options *options)
|
|||
set_nospam(&(m->fr), random_int());
|
||||
set_filter_function(&(m->fr), &friend_already_added, m);
|
||||
|
||||
networking_registerhandler(m->net, NET_PACKET_GROUP_CHATS, &handle_group, m);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/* Run this before closing shop. */
|
||||
void kill_messenger(Messenger *m)
|
||||
{
|
||||
/* FIXME TODO: ideally cleanupMessenger will mirror initMessenger.
|
||||
* This requires the other modules to expose cleanup functions.
|
||||
*/
|
||||
uint32_t i, numchats = m->numchats;
|
||||
|
||||
for (i = 0; i < numchats; ++i)
|
||||
del_groupchat(m, i);
|
||||
uint32_t i;
|
||||
|
||||
kill_friend_connections(m->fr_c);
|
||||
kill_onion(m->onion);
|
||||
kill_onion_announce(m->onion_a);
|
||||
kill_onion_client(m->onion_c);
|
||||
|
@ -2124,8 +1645,6 @@ static int handle_status(void *object, int i, uint8_t status)
|
|||
m->friendlist[i].statusmessage_sent = 0;
|
||||
m->friendlist[i].ping_lastrecv = temp_time;
|
||||
} else { /* Went offline. */
|
||||
m->friendlist[i].crypt_connection_id = -1;
|
||||
|
||||
if (m->friendlist[i].status == FRIEND_ONLINE) {
|
||||
set_friend_status(m, i, FRIEND_CONFIRMED);
|
||||
}
|
||||
|
@ -2425,11 +1944,6 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
|
|||
return -1;
|
||||
|
||||
switch (packet_id) {
|
||||
case PACKET_ID_ALIVE: {
|
||||
m->friendlist[i].ping_lastrecv = temp_time;
|
||||
break;
|
||||
}
|
||||
|
||||
case PACKET_ID_NICKNAME: {
|
||||
if (data_length > MAX_NAME_LENGTH || data_length == 0)
|
||||
break;
|
||||
|
@ -2599,33 +2113,15 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
|
|||
}
|
||||
|
||||
case PACKET_ID_INVITE_GROUPCHAT: {
|
||||
if (data_length != crypto_box_PUBLICKEYBYTES)
|
||||
if (data_length == 0)
|
||||
break;
|
||||
|
||||
if (m->group_invite)
|
||||
(*m->group_invite)(m, i, data, m->group_invite_userdata);
|
||||
(*m->group_invite)(m, i, data, data_length);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PACKET_ID_JOIN_GROUPCHAT: {
|
||||
if (data_length != crypto_box_PUBLICKEYBYTES * 2)
|
||||
break;
|
||||
|
||||
int groupnum = group_num(m, data);
|
||||
|
||||
if (groupnum == -1)
|
||||
break;
|
||||
|
||||
if (!group_invited(m, i, groupnum))
|
||||
break;
|
||||
|
||||
group_newpeer(m->chats[groupnum], data + crypto_box_PUBLICKEYBYTES);
|
||||
/* This is just there to speedup joining. */
|
||||
chat_bootstrap(m->chats[groupnum], get_friend_ipport(m, i), data + crypto_box_PUBLICKEYBYTES);
|
||||
break;
|
||||
}
|
||||
|
||||
case PACKET_ID_FILE_SENDREQUEST: {
|
||||
if (data_length < 1 + sizeof(uint64_t) + 1)
|
||||
break;
|
||||
|
@ -2720,27 +2216,6 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int friend_new_connection(Messenger *m, int32_t friendnumber, const uint8_t *real_public_key)
|
||||
{
|
||||
if (friend_not_valid(m, friendnumber))
|
||||
return -1;
|
||||
|
||||
if (m->friendlist[friendnumber].crypt_connection_id != -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int id = new_crypto_connection(m->net_crypto, real_public_key);
|
||||
|
||||
if (id == -1)
|
||||
return -1;
|
||||
|
||||
m->friendlist[friendnumber].crypt_connection_id = id;
|
||||
connection_status_handler(m->net_crypto, id, &handle_status, m, friendnumber);
|
||||
connection_data_handler(m->net_crypto, id, &handle_packet, m, friendnumber);
|
||||
connection_lossy_data_handler(m->net_crypto, id, &handle_custom_lossy_packet, m, friendnumber);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: Make this function not suck. */
|
||||
void do_friends(Messenger *m)
|
||||
{
|
||||
|
@ -2767,32 +2242,6 @@ void do_friends(Messenger *m)
|
|||
*/
|
||||
check_friend_request_timed_out(m, i, temp_time);
|
||||
}
|
||||
|
||||
friend_new_connection(m, i, m->friendlist[i].client_id);
|
||||
}
|
||||
|
||||
if (m->friendlist[i].crypt_connection_id != -1) {
|
||||
uint8_t dht_public_key1[crypto_box_PUBLICKEYBYTES];
|
||||
uint64_t timestamp1 = onion_getfriend_DHT_pubkey(m->onion_c, m->friendlist[i].onion_friendnum, dht_public_key1);
|
||||
uint8_t dht_public_key2[crypto_box_PUBLICKEYBYTES];
|
||||
uint64_t timestamp2 = get_connection_dht_key(m->net_crypto, m->friendlist[i].crypt_connection_id, dht_public_key2);
|
||||
|
||||
if (timestamp1 > timestamp2) {
|
||||
set_connection_dht_public_key(m->net_crypto, m->friendlist[i].crypt_connection_id, dht_public_key1, timestamp1);
|
||||
} else if (timestamp1 < timestamp2) {
|
||||
onion_set_friend_DHT_pubkey(m->onion_c, m->friendlist[i].onion_friendnum, dht_public_key2, timestamp2);
|
||||
}
|
||||
|
||||
uint8_t direct_connected;
|
||||
unsigned int status = crypto_connection_status(m->net_crypto, m->friendlist[i].crypt_connection_id, &direct_connected);
|
||||
|
||||
if (direct_connected == 0 || status == CRYPTO_CONN_COOKIE_REQUESTING) {
|
||||
IP_Port friendip;
|
||||
|
||||
if (onion_getfriendip(m->onion_c, m->friendlist[i].onion_friendnum, &friendip) == 1) {
|
||||
set_direct_ip_port(m->net_crypto, m->friendlist[i].crypt_connection_id, friendip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m->friendlist[i].status == FRIEND_ONLINE) { /* friend is online. */
|
||||
|
@ -2821,17 +2270,6 @@ void do_friends(Messenger *m)
|
|||
m->friendlist[i].user_istyping_sent = 1;
|
||||
}
|
||||
|
||||
if (m->friendlist[i].ping_lastsent + FRIEND_PING_INTERVAL < temp_time) {
|
||||
send_ping(m, i);
|
||||
}
|
||||
|
||||
if (m->friendlist[i].ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) {
|
||||
/* If we stopped receiving ping packets, kill it. */
|
||||
crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id);
|
||||
m->friendlist[i].crypt_connection_id = -1;
|
||||
set_friend_status(m, i, FRIEND_CONFIRMED);
|
||||
}
|
||||
|
||||
if (m->friendlist[i].share_relays_lastsent + FRIEND_SHARE_RELAYS_INTERVAL < temp_time) {
|
||||
send_relays(m, i);
|
||||
}
|
||||
|
@ -2901,8 +2339,8 @@ void do_messenger(Messenger *m)
|
|||
|
||||
do_net_crypto(m->net_crypto);
|
||||
do_onion_client(m->onion_c);
|
||||
do_friend_connections(m->fr_c);
|
||||
do_friends(m);
|
||||
do_allgroupchats(m);
|
||||
LANdiscovery(m);
|
||||
|
||||
#ifdef LOGGING
|
||||
|
@ -2913,16 +2351,6 @@ void do_messenger(Messenger *m)
|
|||
Assoc_status(m->dht->assoc);
|
||||
#endif
|
||||
|
||||
if (m->numchats > 0) {
|
||||
size_t c;
|
||||
|
||||
for (c = 0; c < m->numchats; c++) {
|
||||
if (m->chats[c])
|
||||
Assoc_status(m->chats[c]->assoc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lastdump = unix_time();
|
||||
uint32_t client, last_pinged;
|
||||
|
||||
|
@ -2992,8 +2420,8 @@ void do_messenger(Messenger *m)
|
|||
if (ping_lastrecv > 999)
|
||||
ping_lastrecv = 999;
|
||||
|
||||
LOGGER_INFO("F[%2u:%2u] <%s> %02i [%03u] %s",
|
||||
dht2m[friend], friend, msgfptr->name, msgfptr->crypt_connection_id,
|
||||
LOGGER_INFO("F[%2u:%2u] <%s> [%03u] %s",
|
||||
dht2m[friend], friend, msgfptr->name,
|
||||
ping_lastrecv, ID2String(msgfptr->client_id));
|
||||
} else {
|
||||
LOGGER_INFO("F[--:%2u] %s", friend, ID2String(dhtfptr->client_id));
|
||||
|
@ -3429,51 +2857,3 @@ int get_friendlist(const Messenger *m, int32_t **out_list, uint32_t *out_list_le
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the number of chats in the instance m.
|
||||
* You should use this to determine how much memory to allocate
|
||||
* for copy_chatlist. */
|
||||
uint32_t count_chatlist(const Messenger *m)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < m->numchats; i++) {
|
||||
if (m->chats[i]) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Copy a list of valid chat IDs into the array out_list.
|
||||
* If out_list is NULL, returns 0.
|
||||
* Otherwise, returns the number of elements copied.
|
||||
* If the array was too small, the contents
|
||||
* of out_list will be truncated to list_size. */
|
||||
uint32_t copy_chatlist(const Messenger *m, int *out_list, uint32_t list_size)
|
||||
{
|
||||
if (!out_list)
|
||||
return 0;
|
||||
|
||||
if (m->numchats == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t i;
|
||||
uint32_t ret = 0;
|
||||
|
||||
for (i = 0; i < m->numchats; i++) {
|
||||
if (ret >= list_size) {
|
||||
break; /* Abandon ship */
|
||||
}
|
||||
|
||||
if (m->chats[i]) {
|
||||
out_list[ret] = i;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -26,12 +26,9 @@
|
|||
#ifndef MESSENGER_H
|
||||
#define MESSENGER_H
|
||||
|
||||
#include "net_crypto.h"
|
||||
#include "DHT.h"
|
||||
#include "friend_requests.h"
|
||||
#include "LAN_discovery.h"
|
||||
#include "group_chats.h"
|
||||
#include "onion_client.h"
|
||||
#include "friend_connection.h"
|
||||
|
||||
#define MAX_NAME_LENGTH 128
|
||||
/* TODO: this must depend on other variable. */
|
||||
|
@ -42,8 +39,7 @@
|
|||
|
||||
#define FRIEND_ADDRESS_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + sizeof(uint16_t))
|
||||
|
||||
/* NOTE: Packet ids below 16 must never be used. */
|
||||
#define PACKET_ID_ALIVE 16
|
||||
/* NOTE: Packet ids below 17 must never be used. */
|
||||
#define PACKET_ID_SHARE_RELAYS 17
|
||||
#define PACKET_ID_NICKNAME 48
|
||||
#define PACKET_ID_STATUSMESSAGE 49
|
||||
|
@ -61,12 +57,9 @@
|
|||
#define PACKET_ID_FILE_SENDREQUEST 80
|
||||
#define PACKET_ID_FILE_CONTROL 81
|
||||
#define PACKET_ID_FILE_DATA 82
|
||||
#define PACKET_ID_INVITE_GROUPCHAT 144
|
||||
#define PACKET_ID_JOIN_GROUPCHAT 145
|
||||
#define PACKET_ID_ACCEPT_GROUPCHAT 146
|
||||
#define PACKET_ID_INVITE_GROUPCHAT 96
|
||||
#define PACKET_ID_MESSAGE_GROUPCHAT 97
|
||||
|
||||
/* Max number of groups we can invite someone at the same time to. */
|
||||
#define MAX_INVITED_GROUPS 64
|
||||
|
||||
/* Max number of tcp relays sent to friends */
|
||||
#define MAX_SHARED_RELAYS 16
|
||||
|
@ -108,15 +101,9 @@ enum {
|
|||
/* Default start timeout in seconds between friend requests. */
|
||||
#define FRIENDREQUEST_TIMEOUT 5;
|
||||
|
||||
/* Interval between the sending of ping packets. */
|
||||
#define FRIEND_PING_INTERVAL 6
|
||||
|
||||
/* Interval between the sending of tcp relay information */
|
||||
#define FRIEND_SHARE_RELAYS_INTERVAL (5 * 60)
|
||||
|
||||
/* If no packets are received from friend in this time interval, kill the connection. */
|
||||
#define FRIEND_CONNECTION_TIMEOUT (FRIEND_PING_INTERVAL * 3)
|
||||
|
||||
/* Must be < MAX_CRYPTO_DATA_SIZE */
|
||||
#define AVATAR_DATA_MAX_CHUNK_SIZE (MAX_CRYPTO_DATA_SIZE-1)
|
||||
|
||||
|
@ -199,9 +186,9 @@ enum {
|
|||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t client_id[CLIENT_ID_SIZE];
|
||||
uint32_t onion_friendnum;
|
||||
int crypt_connection_id;
|
||||
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||
int friendcon_id;
|
||||
|
||||
uint64_t friendrequest_lastsent; // Time at which the last friend request was sent.
|
||||
uint32_t friendrequest_timeout; // The timeout between successful friendrequest sending attempts.
|
||||
uint8_t status; // 0 if no friend, 1 if added, 2 if friend request sent, 3 if confirmed friend, 4 if online.
|
||||
|
@ -222,13 +209,10 @@ typedef struct {
|
|||
uint32_t message_id; // a semi-unique id used in read receipts.
|
||||
uint8_t receives_read_receipts; // shall we send read receipts to this person?
|
||||
uint32_t friendrequest_nospam; // The nospam number used in the friend request.
|
||||
uint64_t ping_lastrecv;
|
||||
uint64_t ping_lastsent;
|
||||
uint64_t ping_lastrecv;//TODO remove
|
||||
uint64_t share_relays_lastsent;
|
||||
struct File_Transfers file_sending[MAX_CONCURRENT_FILE_PIPES];
|
||||
struct File_Transfers file_receiving[MAX_CONCURRENT_FILE_PIPES];
|
||||
int invited_groups[MAX_INVITED_GROUPS];
|
||||
uint16_t invited_groups_num;
|
||||
|
||||
AVATAR_SENDDATA avatar_send_data;
|
||||
AVATAR_RECEIVEDATA *avatar_recv_data; // We are receiving avatar data from this friend.
|
||||
|
@ -255,6 +239,8 @@ typedef struct Messenger {
|
|||
Onion_Announce *onion_a;
|
||||
Onion_Client *onion_c;
|
||||
|
||||
Friend_Connections *fr_c;
|
||||
|
||||
Friend_Requests fr;
|
||||
uint8_t name[MAX_NAME_LENGTH];
|
||||
uint16_t name_length;
|
||||
|
@ -274,9 +260,6 @@ typedef struct Messenger {
|
|||
|
||||
uint32_t numonline_friends;
|
||||
|
||||
Group_Chat **chats;
|
||||
uint32_t numchats;
|
||||
|
||||
uint64_t last_LANdiscovery;
|
||||
|
||||
#define NUM_SAVED_TCP_RELAYS 8
|
||||
|
@ -308,14 +291,9 @@ typedef struct Messenger {
|
|||
void *avatar_data_recv_userdata;
|
||||
void (*avatar_data_recv)(struct Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *, uint32_t, void *);
|
||||
|
||||
void (*group_invite)(struct Messenger *m, int32_t, const uint8_t *, void *);
|
||||
void *group_invite_userdata;
|
||||
void (*group_message)(struct Messenger *m, int, int, const uint8_t *, uint16_t, void *);
|
||||
void *group_message_userdata;
|
||||
void (*group_action)(struct Messenger *m, int, int, const uint8_t *, uint16_t, void *);
|
||||
void *group_action_userdata;
|
||||
void (*group_namelistchange)(struct Messenger *m, int, int, uint8_t, void *);
|
||||
void *group_namelistchange_userdata;
|
||||
void *group_chat_object; /* Set by new_groupchats()*/
|
||||
void (*group_invite)(struct Messenger *m, int32_t, const uint8_t *, uint16_t);
|
||||
void (*group_message)(struct Messenger *m, int32_t, const uint8_t *, uint16_t);
|
||||
|
||||
void (*file_sendrequest)(struct Messenger *m, int32_t, uint8_t, uint64_t, const uint8_t *, uint16_t, void *);
|
||||
void *file_sendrequest_userdata;
|
||||
|
@ -518,6 +496,11 @@ uint8_t m_get_self_userstatus(const Messenger *m);
|
|||
*/
|
||||
int m_set_avatar(Messenger *m, uint8_t format, const uint8_t *data, uint32_t length);
|
||||
|
||||
/* Unsets the user avatar.
|
||||
|
||||
returns 0 on success (currently always returns 0) */
|
||||
int m_unset_avatar(Messenger *m);
|
||||
|
||||
/* Get avatar data from the current user.
|
||||
* Copies the current user avatar data to the destination buffer and sets the image format
|
||||
* accordingly.
|
||||
|
@ -747,97 +730,16 @@ void m_callback_avatar_data(Messenger *m, void (*function)(Messenger *m, int32_t
|
|||
|
||||
/* Set the callback for group invites.
|
||||
*
|
||||
* Function(Messenger *m, int32_t friendnumber, uint8_t *group_public_key, void *userdata)
|
||||
* Function(Messenger *m, int32_t friendnumber, uint8_t *data, uint16_t length)
|
||||
*/
|
||||
void m_callback_group_invite(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, void *),
|
||||
void *userdata);
|
||||
void m_callback_group_invite(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t));
|
||||
|
||||
/* Set the callback for group messages.
|
||||
/* Send a group invite packet.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata)
|
||||
* return 1 on success
|
||||
* return 0 on failure
|
||||
*/
|
||||
void m_callback_group_message(Messenger *m, void (*function)(Messenger *m, int, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Set the callback for group actions.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata)
|
||||
*/
|
||||
void m_callback_group_action(Messenger *m, void (*function)(Messenger *m, int, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Set callback function for peer name list changes.
|
||||
*
|
||||
* It gets called every time the name list changes(new peer/name, deleted peer)
|
||||
* Function(Tox *tox, int groupnumber, void *userdata)
|
||||
*/
|
||||
void m_callback_group_namelistchange(Messenger *m, void (*function)(Messenger *m, int, int, uint8_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Creates a new groupchat and puts it in the chats array.
|
||||
*
|
||||
* return group number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_groupchat(Messenger *m);
|
||||
|
||||
/* Delete a groupchat from the chats array.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 if failure.
|
||||
*/
|
||||
int del_groupchat(Messenger *m, int groupnumber);
|
||||
|
||||
/* Copy the name of peernumber who is in groupnumber to name.
|
||||
* name must be at least MAX_NICK_BYTES long.
|
||||
*
|
||||
* return length of name if success
|
||||
* return -1 if failure
|
||||
*/
|
||||
int m_group_peername(const Messenger *m, int groupnumber, int peernumber, uint8_t *name);
|
||||
|
||||
/* invite friendnumber to groupnumber
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int invite_friend(Messenger *m, int32_t friendnumber, int groupnumber);
|
||||
|
||||
/* Join a group (you need to have been invited first.)
|
||||
*
|
||||
* returns group number on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int join_groupchat(Messenger *m, int32_t friendnumber, const uint8_t *friend_group_public_key);
|
||||
|
||||
/* send a group message
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_message_send(const Messenger *m, int groupnumber, const uint8_t *message, uint32_t length);
|
||||
|
||||
/* send a group action
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_action_send(const Messenger *m, int groupnumber, const uint8_t *action, uint32_t length);
|
||||
|
||||
/* Return the number of peers in the group chat on success.
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_number_peers(const Messenger *m, int groupnumber);
|
||||
|
||||
/* List all the peers in the group chat.
|
||||
*
|
||||
* Copies the names of the peers to the name[length][MAX_NICK_BYTES] array.
|
||||
*
|
||||
* Copies the lengths of the names to lengths[length]
|
||||
*
|
||||
* returns the number of peers on success.
|
||||
*
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int group_names(const Messenger *m, int groupnumber, uint8_t names[][MAX_NICK_BYTES], uint16_t lengths[],
|
||||
uint16_t length);
|
||||
int send_group_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length);
|
||||
|
||||
/****************FILE SENDING*****************/
|
||||
|
||||
|
@ -1013,16 +915,4 @@ uint32_t copy_friendlist(const Messenger *m, int32_t *out_list, uint32_t list_si
|
|||
*/
|
||||
int get_friendlist(const Messenger *m, int **out_list, uint32_t *out_list_length);
|
||||
|
||||
/* Return the number of chats in the instance m.
|
||||
* You should use this to determine how much memory to allocate
|
||||
* for copy_chatlist. */
|
||||
uint32_t count_chatlist(const Messenger *m);
|
||||
|
||||
/* Copy a list of valid chat IDs into the array out_list.
|
||||
* If out_list is NULL, returns 0.
|
||||
* Otherwise, returns the number of elements copied.
|
||||
* If the array was too small, the contents
|
||||
* of out_list will be truncated to list_size. */
|
||||
uint32_t copy_chatlist(const Messenger *m, int *out_list, uint32_t list_size);
|
||||
|
||||
#endif
|
||||
|
|
565
toxcore/friend_connection.c
Normal file
565
toxcore/friend_connection.c
Normal file
|
@ -0,0 +1,565 @@
|
|||
/* friend_connection.c
|
||||
*
|
||||
* Connection to friends.
|
||||
*
|
||||
* Copyright (C) 2014 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* 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
|
||||
|
||||
#include "friend_connection.h"
|
||||
#include "util.h"
|
||||
|
||||
/* return 1 if the friendcon_id is not valid.
|
||||
* return 0 if the friendcon_id is valid.
|
||||
*/
|
||||
static uint8_t friendconn_id_not_valid(const Friend_Connections *fr_c, int friendcon_id)
|
||||
{
|
||||
if ((unsigned int)friendcon_id >= fr_c->num_cons)
|
||||
return 1;
|
||||
|
||||
if (fr_c->conns == NULL)
|
||||
return 1;
|
||||
|
||||
if (fr_c->conns[friendcon_id].status == FRIENDCONN_STATUS_NONE)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Set the size of the friend connections list to num.
|
||||
*
|
||||
* return -1 if realloc fails.
|
||||
* return 0 if it succeeds.
|
||||
*/
|
||||
static int realloc_friendconns(Friend_Connections *fr_c, uint32_t num)
|
||||
{
|
||||
if (num == 0) {
|
||||
free(fr_c->conns);
|
||||
fr_c->conns = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Friend_Conn *newgroup_cons = realloc(fr_c->conns, num * sizeof(Friend_Conn));
|
||||
|
||||
if (newgroup_cons == NULL)
|
||||
return -1;
|
||||
|
||||
fr_c->conns = newgroup_cons;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a new empty friend connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return friendcon_id on success.
|
||||
*/
|
||||
static int create_friend_conn(Friend_Connections *fr_c)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < fr_c->num_cons; ++i) {
|
||||
if (fr_c->conns[i].status == FRIENDCONN_STATUS_NONE)
|
||||
return i;
|
||||
}
|
||||
|
||||
int id = -1;
|
||||
|
||||
if (realloc_friendconns(fr_c, fr_c->num_cons + 1) == 0) {
|
||||
id = fr_c->num_cons;
|
||||
++fr_c->num_cons;
|
||||
memset(&(fr_c->conns[id]), 0, sizeof(Friend_Conn));
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Wipe a friend connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
static int wipe_friend_conn(Friend_Connections *fr_c, int friendcon_id)
|
||||
{
|
||||
if (friendconn_id_not_valid(fr_c, friendcon_id))
|
||||
return -1;
|
||||
|
||||
uint32_t i;
|
||||
memset(&(fr_c->conns[friendcon_id]), 0 , sizeof(Friend_Conn));
|
||||
|
||||
for (i = fr_c->num_cons; i != 0; --i) {
|
||||
if (fr_c->conns[i - 1].status != FRIENDCONN_STATUS_NONE)
|
||||
break;
|
||||
}
|
||||
|
||||
if (fr_c->num_cons != i) {
|
||||
fr_c->num_cons = i;
|
||||
realloc_friendconns(fr_c, fr_c->num_cons);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id)
|
||||
{
|
||||
if (friendconn_id_not_valid(fr_c, friendcon_id))
|
||||
return 0;
|
||||
|
||||
return &fr_c->conns[friendcon_id];
|
||||
}
|
||||
|
||||
/* return friendcon_id corresponding to the real public key on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int getfriend_conn_id_pk(Friend_Connections *fr_c, const uint8_t *real_pk)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < fr_c->num_cons; ++i) {
|
||||
Friend_Conn *friend_con = get_conn(fr_c, i);
|
||||
|
||||
if (friend_con) {
|
||||
if (memcmp(friend_con->real_public_key, real_pk, crypto_box_PUBLICKEYBYTES) == 0)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* callback for recv TCP relay nodes. */
|
||||
static int tcp_relay_node_callback(void *object, uint32_t number, IP_Port ip_port, const uint8_t *public_key)
|
||||
{
|
||||
Friend_Connections *fr_c = object;
|
||||
Friend_Conn *friend_con = get_conn(fr_c, number);
|
||||
|
||||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
if (friend_con->crypt_connection_id != -1) {
|
||||
return add_tcp_relay_peer(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, public_key);
|
||||
} else {
|
||||
return add_tcp_relay(fr_c->net_crypto, ip_port, public_key);
|
||||
}
|
||||
}
|
||||
|
||||
static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id);
|
||||
/* Callback for DHT ip_port changes. */
|
||||
static void dht_ip_callback(void *object, int32_t number, IP_Port ip_port)
|
||||
{
|
||||
Friend_Connections *fr_c = object;
|
||||
Friend_Conn *friend_con = get_conn(fr_c, number);
|
||||
|
||||
if (!friend_con)
|
||||
return;
|
||||
|
||||
if (friend_con->crypt_connection_id == -1) {
|
||||
friend_new_connection(fr_c, number);
|
||||
}
|
||||
|
||||
set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port);
|
||||
friend_con->dht_ip_port = ip_port;
|
||||
friend_con->dht_ip_port_lastrecv = unix_time();
|
||||
}
|
||||
|
||||
/* Callback for dht public key changes. */
|
||||
static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key)
|
||||
{
|
||||
Friend_Connections *fr_c = object;
|
||||
Friend_Conn *friend_con = get_conn(fr_c, number);
|
||||
|
||||
if (!friend_con)
|
||||
return;
|
||||
|
||||
friend_con->dht_ping_lastrecv = unix_time();
|
||||
|
||||
if (memcmp(friend_con->dht_temp_pk, dht_public_key, crypto_box_PUBLICKEYBYTES) == 0)
|
||||
return;
|
||||
|
||||
if (friend_con->dht_lock) {
|
||||
if (DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock) != 0) {
|
||||
printf("a. Could not delete dht peer. Please report this.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
friend_con->dht_lock = 0;
|
||||
}
|
||||
|
||||
DHT_addfriend(fr_c->dht, dht_public_key, dht_ip_callback, object, number, &friend_con->dht_lock);
|
||||
|
||||
if (friend_con->crypt_connection_id == -1) {
|
||||
friend_new_connection(fr_c, number);
|
||||
}
|
||||
|
||||
set_connection_dht_public_key(fr_c->net_crypto, friend_con->crypt_connection_id, dht_public_key);
|
||||
onion_set_friend_DHT_pubkey(fr_c->onion_c, friend_con->onion_friendnum, dht_public_key);
|
||||
|
||||
memcpy(friend_con->dht_temp_pk, dht_public_key, crypto_box_PUBLICKEYBYTES);
|
||||
}
|
||||
|
||||
static int handle_status(void *object, int number, uint8_t status)
|
||||
{
|
||||
Friend_Connections *fr_c = object;
|
||||
Friend_Conn *friend_con = get_conn(fr_c, number);
|
||||
|
||||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
if (status) { /* Went online. */
|
||||
friend_con->status = FRIENDCONN_STATUS_CONNECTED;
|
||||
friend_con->ping_lastrecv = unix_time();
|
||||
onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
|
||||
} else { /* Went offline. */
|
||||
friend_con->status = FRIENDCONN_STATUS_CONNECTING;
|
||||
friend_con->crypt_connection_id = -1;
|
||||
friend_con->dht_ping_lastrecv = unix_time();
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
|
||||
if (friend_con->callbacks[i].status_callback)
|
||||
friend_con->callbacks[i].status_callback(friend_con->callbacks[i].status_callback_object,
|
||||
friend_con->callbacks[i].status_callback_id, status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_packet(void *object, int number, uint8_t *data, uint16_t length)
|
||||
{
|
||||
if (length == 0)
|
||||
return -1;
|
||||
|
||||
Friend_Connections *fr_c = object;
|
||||
Friend_Conn *friend_con = get_conn(fr_c, number);
|
||||
|
||||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
if (data[0] == PACKET_ID_ALIVE) {
|
||||
friend_con->ping_lastrecv = unix_time();
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
|
||||
if (friend_con->callbacks[i].data_callback)
|
||||
friend_con->callbacks[i].data_callback(friend_con->callbacks[i].data_callback_object,
|
||||
friend_con->callbacks[i].data_callback_id, data, length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_lossy_packet(void *object, int number, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
if (length == 0)
|
||||
return -1;
|
||||
|
||||
Friend_Connections *fr_c = object;
|
||||
Friend_Conn *friend_con = get_conn(fr_c, number);
|
||||
|
||||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
|
||||
if (friend_con->callbacks[i].lossy_data_callback)
|
||||
friend_con->callbacks[i].lossy_data_callback(friend_con->callbacks[i].lossy_data_callback_object,
|
||||
friend_con->callbacks[i].lossy_data_callback_id, data, length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_new_connections(void *object, New_Connection *n_c)
|
||||
{
|
||||
Friend_Connections *fr_c = object;
|
||||
int friendcon_id = getfriend_conn_id_pk(fr_c, n_c->public_key);
|
||||
Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
|
||||
|
||||
if (friend_con) {
|
||||
|
||||
if (friend_con->crypt_connection_id != -1)
|
||||
return -1;
|
||||
|
||||
int id = accept_crypto_connection(fr_c->net_crypto, n_c);
|
||||
connection_status_handler(fr_c->net_crypto, id, &handle_status, fr_c, friendcon_id);
|
||||
connection_data_handler(fr_c->net_crypto, id, &handle_packet, fr_c, friendcon_id);
|
||||
connection_lossy_data_handler(fr_c->net_crypto, id, &handle_lossy_packet, fr_c, friendcon_id);
|
||||
friend_con->crypt_connection_id = id;
|
||||
|
||||
if (n_c->source.ip.family != AF_INET && n_c->source.ip.family != AF_INET6) {
|
||||
set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port);
|
||||
} else {
|
||||
friend_con->dht_ip_port = n_c->source;
|
||||
friend_con->dht_ip_port_lastrecv = unix_time();
|
||||
}
|
||||
|
||||
dht_pk_callback(fr_c, friendcon_id, n_c->dht_public_key);
|
||||
|
||||
nc_dht_pk_callback(fr_c->net_crypto, id, &dht_pk_callback, fr_c, friendcon_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id)
|
||||
{
|
||||
Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
|
||||
|
||||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
if (friend_con->crypt_connection_id != -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int id = new_crypto_connection(fr_c->net_crypto, friend_con->real_public_key);
|
||||
|
||||
if (id == -1)
|
||||
return -1;
|
||||
|
||||
friend_con->crypt_connection_id = id;
|
||||
connection_status_handler(fr_c->net_crypto, id, &handle_status, fr_c, friendcon_id);
|
||||
connection_data_handler(fr_c->net_crypto, id, &handle_packet, fr_c, friendcon_id);
|
||||
connection_lossy_data_handler(fr_c->net_crypto, id, &handle_lossy_packet, fr_c, friendcon_id);
|
||||
nc_dht_pk_callback(fr_c->net_crypto, id, &dht_pk_callback, fr_c, friendcon_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_ping(const Friend_Connections *fr_c, int friendcon_id)
|
||||
{
|
||||
Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
|
||||
|
||||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
uint8_t ping = PACKET_ID_ALIVE;
|
||||
int64_t ret = write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, &ping, sizeof(ping), 0);
|
||||
|
||||
if (ret != -1) {
|
||||
friend_con->ping_lastsent = unix_time();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the callbacks for the friend connection.
|
||||
* index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure
|
||||
*/
|
||||
int friend_connection_callbacks(Friend_Connections *fr_c, int friendcon_id, unsigned int index,
|
||||
int (*status_callback)(void *object, int id, uint8_t status), int (*data_callback)(void *object, int id, uint8_t *data,
|
||||
uint16_t length), int (*lossy_data_callback)(void *object, int id, const uint8_t *data, uint16_t length), void *object,
|
||||
int number)
|
||||
{
|
||||
Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
|
||||
|
||||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
if (index >= MAX_FRIEND_CONNECTION_CALLBACKS)
|
||||
return -1;
|
||||
|
||||
friend_con->callbacks[index].status_callback = status_callback;
|
||||
friend_con->callbacks[index].data_callback = data_callback;
|
||||
friend_con->callbacks[index].lossy_data_callback = lossy_data_callback;
|
||||
|
||||
friend_con->callbacks[index].status_callback_object =
|
||||
friend_con->callbacks[index].data_callback_object =
|
||||
friend_con->callbacks[index].lossy_data_callback_object = object;
|
||||
|
||||
friend_con->callbacks[index].status_callback_id =
|
||||
friend_con->callbacks[index].data_callback_id =
|
||||
friend_con->callbacks[index].lossy_data_callback_id = number;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return the crypt_connection_id for the connection.
|
||||
*
|
||||
* return crypt_connection_id on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int friend_connection_crypt_connection_id(Friend_Connections *fr_c, int friendcon_id)
|
||||
{
|
||||
Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
|
||||
|
||||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
return friend_con->crypt_connection_id;
|
||||
}
|
||||
|
||||
/* Create a new friend connection.
|
||||
* If one to that real public key already exists, increase lock count and return it.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return connection id on success.
|
||||
*/
|
||||
int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key)
|
||||
{
|
||||
int friendcon_id = getfriend_conn_id_pk(fr_c, real_public_key);
|
||||
|
||||
if (friendcon_id != -1) {
|
||||
++fr_c->conns[friendcon_id].lock_count;
|
||||
return friendcon_id;
|
||||
}
|
||||
|
||||
friendcon_id = create_friend_conn(fr_c);
|
||||
|
||||
if (friendcon_id == -1)
|
||||
return -1;
|
||||
|
||||
int32_t onion_friendnum = onion_addfriend(fr_c->onion_c, real_public_key);
|
||||
|
||||
if (onion_friendnum == -1)
|
||||
return -1;
|
||||
|
||||
Friend_Conn *friend_con = &fr_c->conns[friendcon_id];
|
||||
|
||||
friend_con->crypt_connection_id = -1;
|
||||
friend_con->status = FRIENDCONN_STATUS_CONNECTING;
|
||||
memcpy(friend_con->real_public_key, real_public_key, crypto_box_PUBLICKEYBYTES);
|
||||
friend_con->onion_friendnum = onion_friendnum;
|
||||
|
||||
recv_tcp_relay_handler(fr_c->onion_c, onion_friendnum, &tcp_relay_node_callback, fr_c, friendcon_id);
|
||||
onion_dht_pk_callback(fr_c->onion_c, onion_friendnum, &dht_pk_callback, fr_c, friendcon_id);
|
||||
|
||||
return friendcon_id;
|
||||
}
|
||||
|
||||
/* Kill a friend connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id)
|
||||
{
|
||||
Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
|
||||
|
||||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
if (friend_con->lock_count) {
|
||||
--friend_con->lock_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
onion_delfriend(fr_c->onion_c, friend_con->onion_friendnum);
|
||||
crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
|
||||
|
||||
if (friend_con->dht_lock) {
|
||||
DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
|
||||
}
|
||||
|
||||
return wipe_friend_conn(fr_c, friendcon_id);
|
||||
}
|
||||
|
||||
|
||||
/* Create new friend_connections instance. */
|
||||
Friend_Connections *new_friend_connections(Onion_Client *onion_c)
|
||||
{
|
||||
if (!onion_c)
|
||||
return NULL;
|
||||
|
||||
Friend_Connections *temp = calloc(1, sizeof(Friend_Connections));
|
||||
|
||||
if (temp == NULL)
|
||||
return NULL;
|
||||
|
||||
temp->dht = onion_c->dht;
|
||||
temp->net_crypto = onion_c->c;
|
||||
temp->onion_c = onion_c;
|
||||
|
||||
new_connection_handler(temp->net_crypto, &handle_new_connections, temp);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* main friend_connections loop. */
|
||||
void do_friend_connections(Friend_Connections *fr_c)
|
||||
{
|
||||
uint32_t i;
|
||||
uint64_t temp_time = unix_time();
|
||||
|
||||
for (i = 0; i < fr_c->num_cons; ++i) {
|
||||
Friend_Conn *friend_con = get_conn(fr_c, i);
|
||||
|
||||
if (friend_con) {
|
||||
if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) {
|
||||
if (friend_con->dht_ping_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
|
||||
if (friend_con->dht_lock) {
|
||||
DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
|
||||
friend_con->dht_lock = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (friend_con->dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
|
||||
friend_con->dht_ip_port.ip.family = 0;
|
||||
}
|
||||
|
||||
if (friend_con->dht_lock) {
|
||||
if (friend_new_connection(fr_c, i) == 0) {
|
||||
set_connection_dht_public_key(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_temp_pk);
|
||||
set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) {
|
||||
if (friend_con->ping_lastsent + FRIEND_PING_INTERVAL < temp_time) {
|
||||
send_ping(fr_c, i);
|
||||
}
|
||||
|
||||
if (friend_con->ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) {
|
||||
/* If we stopped receiving ping packets, kill it. */
|
||||
crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
|
||||
friend_con->crypt_connection_id = -1;
|
||||
handle_status(fr_c, i, 0); /* Going offline. */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Free everything related with friend_connections. */
|
||||
void kill_friend_connections(Friend_Connections *fr_c)
|
||||
{
|
||||
if (!fr_c)
|
||||
return;
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < fr_c->num_cons; ++i) {
|
||||
kill_friend_connection(fr_c, i);
|
||||
}
|
||||
|
||||
free(fr_c);
|
||||
}
|
140
toxcore/friend_connection.h
Normal file
140
toxcore/friend_connection.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
/* friend_connection.h
|
||||
*
|
||||
* Connection to friends.
|
||||
*
|
||||
* Copyright (C) 2014 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FRIEND_CONNECTION_H
|
||||
#define FRIEND_CONNECTION_H
|
||||
|
||||
#include "net_crypto.h"
|
||||
#include "DHT.h"
|
||||
#include "LAN_discovery.h"
|
||||
#include "onion_client.h"
|
||||
|
||||
|
||||
#define MAX_FRIEND_CONNECTION_CALLBACKS 2
|
||||
#define MESSENGER_CALLBACK_INDEX 0
|
||||
#define GROUPCHAT_CALLBACK_INDEX 1
|
||||
|
||||
#define PACKET_ID_ALIVE 16
|
||||
|
||||
/* Interval between the sending of ping packets. */
|
||||
#define FRIEND_PING_INTERVAL 6
|
||||
|
||||
/* If no packets are received from friend in this time interval, kill the connection. */
|
||||
#define FRIEND_CONNECTION_TIMEOUT (FRIEND_PING_INTERVAL * 3)
|
||||
|
||||
/* Time before friend is removed from the DHT after last hearing about him. */
|
||||
#define FRIEND_DHT_TIMEOUT BAD_NODE_TIMEOUT
|
||||
|
||||
|
||||
enum {
|
||||
FRIENDCONN_STATUS_NONE,
|
||||
FRIENDCONN_STATUS_CONNECTING,
|
||||
FRIENDCONN_STATUS_CONNECTED
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t status;
|
||||
|
||||
uint8_t real_public_key[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t dht_temp_pk[crypto_box_PUBLICKEYBYTES];
|
||||
uint16_t dht_lock;
|
||||
IP_Port dht_ip_port;
|
||||
uint64_t dht_ping_lastrecv, dht_ip_port_lastrecv;
|
||||
|
||||
int onion_friendnum;
|
||||
int crypt_connection_id;
|
||||
|
||||
uint64_t ping_lastrecv, ping_lastsent;
|
||||
|
||||
struct {
|
||||
int (*status_callback)(void *object, int id, uint8_t status);
|
||||
void *status_callback_object;
|
||||
int status_callback_id;
|
||||
|
||||
int (*data_callback)(void *object, int id, uint8_t *data, uint16_t length);
|
||||
void *data_callback_object;
|
||||
int data_callback_id;
|
||||
|
||||
int (*lossy_data_callback)(void *object, int id, const uint8_t *data, uint16_t length);
|
||||
void *lossy_data_callback_object;
|
||||
int lossy_data_callback_id;
|
||||
} callbacks[MAX_FRIEND_CONNECTION_CALLBACKS];
|
||||
|
||||
uint16_t lock_count;
|
||||
} Friend_Conn;
|
||||
|
||||
|
||||
typedef struct {
|
||||
Net_Crypto *net_crypto;
|
||||
DHT *dht;
|
||||
Onion_Client *onion_c;
|
||||
|
||||
Friend_Conn *conns;
|
||||
uint32_t num_cons;
|
||||
|
||||
} Friend_Connections;
|
||||
|
||||
/* Set the callbacks for the friend connection.
|
||||
* index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure
|
||||
*/
|
||||
int friend_connection_callbacks(Friend_Connections *fr_c, int friendcon_id, unsigned int index,
|
||||
int (*status_callback)(void *object, int id, uint8_t status), int (*data_callback)(void *object, int id, uint8_t *data,
|
||||
uint16_t length), int (*lossy_data_callback)(void *object, int id, const uint8_t *data, uint16_t length), void *object,
|
||||
int number);
|
||||
|
||||
/* return the crypt_connection_id for the connection.
|
||||
*
|
||||
* return crypt_connection_id on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int friend_connection_crypt_connection_id(Friend_Connections *fr_c, int friendcon_id);
|
||||
|
||||
/* Create a new friend connection.
|
||||
* If one to that real public key already exists, increase lock count and return it.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return connection id on success.
|
||||
*/
|
||||
int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key);
|
||||
|
||||
/* Kill a friend connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id);
|
||||
|
||||
/* Create new friend_connections instance. */
|
||||
Friend_Connections *new_friend_connections(Onion_Client *onion_c);
|
||||
|
||||
/* main friend_connections loop. */
|
||||
void do_friend_connections(Friend_Connections *fr_c);
|
||||
|
||||
/* Free everything related with friend_connections. */
|
||||
void kill_friend_connections(Friend_Connections *fr_c);
|
||||
|
||||
#endif
|
781
toxcore/group.c
Normal file
781
toxcore/group.c
Normal file
|
@ -0,0 +1,781 @@
|
|||
/* group.c
|
||||
*
|
||||
* Slightly better groupchats implementation.
|
||||
*
|
||||
* Copyright (C) 2014 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* 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
|
||||
|
||||
#include "group.h"
|
||||
#include "util.h"
|
||||
|
||||
/* return 1 if the groupnumber is not valid.
|
||||
* return 0 if the groupnumber is valid.
|
||||
*/
|
||||
static uint8_t groupnumber_not_valid(const Group_Chats *g_c, int groupnumber)
|
||||
{
|
||||
if ((unsigned int)groupnumber >= g_c->num_chats)
|
||||
return 1;
|
||||
|
||||
if (g_c->chats == NULL)
|
||||
return 1;
|
||||
|
||||
if (g_c->chats[groupnumber].status == GROUPCHAT_STATUS_NONE)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Set the size of the groupchat list to num.
|
||||
*
|
||||
* return -1 if realloc fails.
|
||||
* return 0 if it succeeds.
|
||||
*/
|
||||
static int realloc_groupchats(Group_Chats *g_c, uint32_t num)
|
||||
{
|
||||
if (num == 0) {
|
||||
free(g_c->chats);
|
||||
g_c->chats = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Group_c *newgroup_chats = realloc(g_c->chats, num * sizeof(Group_c));
|
||||
|
||||
if (newgroup_chats == NULL)
|
||||
return -1;
|
||||
|
||||
g_c->chats = newgroup_chats;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Create a new empty groupchat connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return groupnumber on success.
|
||||
*/
|
||||
static int create_group_chat(Group_Chats *g_c)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < g_c->num_chats; ++i) {
|
||||
if (g_c->chats[i].status == GROUPCHAT_STATUS_NONE)
|
||||
return i;
|
||||
}
|
||||
|
||||
int id = -1;
|
||||
|
||||
if (realloc_groupchats(g_c, g_c->num_chats + 1) == 0) {
|
||||
id = g_c->num_chats;
|
||||
++g_c->num_chats;
|
||||
memset(&(g_c->chats[id]), 0, sizeof(Group_c));
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
/* Wipe a groupchat.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
static int wipe_group_chat(Group_Chats *g_c, int groupnumber)
|
||||
{
|
||||
if (groupnumber_not_valid(g_c, groupnumber))
|
||||
return -1;
|
||||
|
||||
uint32_t i;
|
||||
memset(&(g_c->chats[groupnumber]), 0 , sizeof(Group_c));
|
||||
|
||||
for (i = g_c->num_chats; i != 0; --i) {
|
||||
if (g_c->chats[i - 1].status != GROUPCHAT_STATUS_NONE)
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_c->num_chats != i) {
|
||||
g_c->num_chats = i;
|
||||
realloc_groupchats(g_c, g_c->num_chats);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Group_c *get_group_c(const Group_Chats *g_c, int groupnumber)
|
||||
{
|
||||
if (groupnumber_not_valid(g_c, groupnumber))
|
||||
return 0;
|
||||
|
||||
return &g_c->chats[groupnumber];
|
||||
}
|
||||
|
||||
/*
|
||||
* check if peer with client_id is in peer array.
|
||||
*
|
||||
* return peer index if peer is in chat.
|
||||
* return -1 if peer is not in chat.
|
||||
*
|
||||
* TODO: make this more efficient.
|
||||
*/
|
||||
|
||||
static int peer_in_chat(const Group_c *chat, const uint8_t *client_id)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < chat->numpeers; ++i)
|
||||
if (id_equal(chat->group[i].client_id, client_id))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if group with identifier is in group array.
|
||||
*
|
||||
* return group number if peer is in list.
|
||||
* return -1 if group is not in list.
|
||||
*
|
||||
* TODO: make this more efficient and maybe use constant time comparisons?
|
||||
*/
|
||||
static int get_group_num(const Group_Chats *g_c, const uint8_t *identifier)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < g_c->num_chats; ++i)
|
||||
if (memcmp(g_c->chats[i].identifier, identifier, GROUP_IDENTIFIER_LENGTH) == 0)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if peer with peer_number is in peer array.
|
||||
*
|
||||
* return peer number if peer is in chat.
|
||||
* return -1 if peer is not in chat.
|
||||
*
|
||||
* TODO: make this more efficient.
|
||||
*/
|
||||
int get_peer_index(Group_c *g, uint16_t peer_number)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < g->numpeers; ++i)
|
||||
if (g->group[i].peer_number == peer_number)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a peer to the group chat.
|
||||
*
|
||||
* return peer_index if success or peer already in chat.
|
||||
* return -1 if error.
|
||||
*/
|
||||
static int addpeer(Group_c *chat, const uint8_t *client_id, uint16_t peer_number)
|
||||
{
|
||||
//TODO
|
||||
//int peer_index = peer_in_chat(chat, client_id);
|
||||
|
||||
//if (peer_index != -1)
|
||||
// return peer_index;
|
||||
|
||||
Group_Peer *temp;
|
||||
temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers + 1));
|
||||
|
||||
if (temp == NULL)
|
||||
return -1;
|
||||
|
||||
memset(&(temp[chat->numpeers]), 0, sizeof(Group_Peer));
|
||||
chat->group = temp;
|
||||
|
||||
id_copy(chat->group[chat->numpeers].client_id, client_id);
|
||||
chat->group[chat->numpeers].peer_number = peer_number;
|
||||
|
||||
chat->group[chat->numpeers].last_recv = unix_time();
|
||||
chat->group[chat->numpeers].last_recv_msgping = unix_time();
|
||||
++chat->numpeers;
|
||||
|
||||
//if (chat->peer_namelistchange != NULL)
|
||||
// (*chat->peer_namelistchange)(chat, chat->numpeers - 1, CHAT_CHANGE_PEER_ADD, chat->group_namelistchange_userdata);
|
||||
|
||||
return (chat->numpeers - 1);
|
||||
}
|
||||
|
||||
static int handle_packet(void *object, int number, uint8_t *data, uint16_t length);
|
||||
|
||||
/* Add friend to group chat.
|
||||
*
|
||||
* return 0 on success
|
||||
* return -1 on failure.
|
||||
*/
|
||||
static int add_friend_to_groupchat(Group_Chats *g_c, int32_t friendnumber, int groupnumber, uint16_t other_groupnum)
|
||||
{
|
||||
if (!m_friend_exists(g_c->m, friendnumber))
|
||||
return -1;
|
||||
|
||||
Group_c *g = get_group_c(g_c, groupnumber);
|
||||
|
||||
if (!g)
|
||||
return -1;
|
||||
|
||||
uint16_t i, ind = MAX_GROUP_CONNECTIONS;
|
||||
|
||||
for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
|
||||
if (g->close[i].type == GROUPCHAT_CLOSE_NONE) {
|
||||
ind = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g->close[i].type == GROUPCHAT_CLOSE_CONNECTION && g->close[i].number == (uint32_t)friendnumber) {
|
||||
g->close[i].group_number = other_groupnum; /* update groupnum. */
|
||||
return 0; /* Already in list. */
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (ind == MAX_GROUP_CONNECTIONS)
|
||||
return -1;
|
||||
|
||||
g->close[ind].type = GROUPCHAT_CLOSE_CONNECTION;
|
||||
g->close[ind].number = friendnumber;
|
||||
g->close[ind].group_number = other_groupnum;
|
||||
int friendcon_id = g_c->m->friendlist[friendnumber].friendcon_id;
|
||||
//TODO
|
||||
friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, 0, &handle_packet, 0, g_c->m,
|
||||
friendnumber);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Creates a new groupchat and puts it in the chats array.
|
||||
*
|
||||
* return group number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_groupchat(Group_Chats *g_c)
|
||||
{
|
||||
int groupnumber = create_group_chat(g_c);
|
||||
|
||||
if (groupnumber == -1)
|
||||
return -1;
|
||||
|
||||
Group_c *g = &g_c->chats[groupnumber];
|
||||
|
||||
g->status = GROUPCHAT_STATUS_VALID;
|
||||
new_symmetric_key(g->identifier);
|
||||
g->peer_number = 0; /* Founder is peer 0. */
|
||||
return groupnumber;
|
||||
}
|
||||
|
||||
/* Delete a groupchat from the chats array.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 if failure.
|
||||
*/
|
||||
int del_groupchat(Group_Chats *g_c, int groupnumber)
|
||||
{
|
||||
Group_c *g = get_group_c(g_c, groupnumber);
|
||||
|
||||
if (!g)
|
||||
return -1;
|
||||
|
||||
free(g->group);
|
||||
return wipe_group_chat(g_c, groupnumber);
|
||||
}
|
||||
|
||||
/* Send a group message packet.
|
||||
*
|
||||
* return 1 on success
|
||||
* return 0 on failure
|
||||
*/
|
||||
int send_group_message_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
if (length >= MAX_CRYPTO_DATA_SIZE)
|
||||
return 0;
|
||||
|
||||
uint8_t packet[1 + length];
|
||||
packet[0] = PACKET_ID_MESSAGE_GROUPCHAT;
|
||||
memcpy(packet + 1, data, length);
|
||||
return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
|
||||
m->friendlist[friendnumber].friendcon_id), packet, sizeof(packet), 0) != -1;
|
||||
}
|
||||
|
||||
#define INVITE_PACKET_SIZE (1 + sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH)
|
||||
#define INVITE_ID 0
|
||||
|
||||
#define INVITE_RESPONSE_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + GROUP_IDENTIFIER_LENGTH)
|
||||
#define INVITE_RESPONSE_ID 1
|
||||
|
||||
/* invite friendnumber to groupnumber
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int invite_friend(Group_Chats *g_c, int32_t friendnumber, int groupnumber)
|
||||
{
|
||||
Group_c *g = get_group_c(g_c, groupnumber);
|
||||
|
||||
if (!g)
|
||||
return -1;
|
||||
|
||||
uint8_t invite[INVITE_PACKET_SIZE];
|
||||
invite[0] = INVITE_ID;
|
||||
uint16_t groupchat_num = htons((uint16_t)groupnumber);
|
||||
memcpy(invite + 1, &groupchat_num, sizeof(groupchat_num));
|
||||
memcpy(invite + 1 + sizeof(groupchat_num), g->identifier, GROUP_IDENTIFIER_LENGTH);
|
||||
|
||||
if (send_group_invite_packet(g_c->m, friendnumber, invite, sizeof(invite))) {
|
||||
return 0;
|
||||
} else {
|
||||
wipe_group_chat(g_c, groupnumber);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Join a group (you need to have been invited first.)
|
||||
*
|
||||
* returns group number on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int join_groupchat(Group_Chats *g_c, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
if (length != sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH)
|
||||
return -1;
|
||||
|
||||
int groupnumber = create_group_chat(g_c);
|
||||
|
||||
if (groupnumber == -1)
|
||||
return -1;
|
||||
|
||||
Group_c *g = &g_c->chats[groupnumber];
|
||||
|
||||
uint16_t group_num = htons(groupnumber);
|
||||
g->status = GROUPCHAT_STATUS_VALID;
|
||||
uint8_t response[INVITE_RESPONSE_PACKET_SIZE];
|
||||
response[0] = INVITE_RESPONSE_ID;
|
||||
memcpy(response + 1, &group_num, sizeof(uint16_t));
|
||||
memcpy(response + 1 + sizeof(uint16_t), data, sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH);
|
||||
|
||||
if (send_group_invite_packet(g_c->m, friendnumber, response, sizeof(response))) {
|
||||
uint16_t other_groupnum;
|
||||
memcpy(&other_groupnum, data, sizeof(other_groupnum));
|
||||
other_groupnum = htons(other_groupnum);
|
||||
memcpy(g->identifier, data + sizeof(uint16_t), GROUP_IDENTIFIER_LENGTH);
|
||||
add_friend_to_groupchat(g_c, friendnumber, groupnumber, other_groupnum);
|
||||
g->peer_number = rand(); /* TODO */
|
||||
return groupnumber;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the callback for group invites.
|
||||
*
|
||||
* Function(Group_Chats *g_c, int32_t friendnumber, uint8_t *data, uint16_t length, void *userdata)
|
||||
*
|
||||
* data of length is what needs to be passed to join_groupchat().
|
||||
*/
|
||||
void g_callback_group_invite(Group_Chats *g_c, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t,
|
||||
void *), void *userdata)
|
||||
{
|
||||
g_c->invite_callback = function;
|
||||
g_c->invite_callback_userdata = userdata;
|
||||
}
|
||||
|
||||
/* Set the callback for group messages.
|
||||
*
|
||||
* Function(Group_Chats *g_c, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata)
|
||||
*/
|
||||
void g_callback_group_message(Group_Chats *g_c, void (*function)(Messenger *m, int, int, const uint8_t *, uint16_t,
|
||||
void *), void *userdata)
|
||||
{
|
||||
g_c->message_callback = function;
|
||||
g_c->message_callback_userdata = userdata;
|
||||
}
|
||||
|
||||
static void handle_friend_invite_packet(Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
Group_Chats *g_c = m->group_chat_object;
|
||||
|
||||
if (length <= 1)
|
||||
return;
|
||||
|
||||
const uint8_t *invite_data = data + 1;
|
||||
uint16_t invite_length = length - 1;
|
||||
|
||||
switch (data[0]) {
|
||||
case INVITE_ID: {
|
||||
if (length != INVITE_PACKET_SIZE)
|
||||
return;
|
||||
|
||||
int groupnumber = get_group_num(g_c, data + 1 + sizeof(uint16_t));
|
||||
|
||||
if (groupnumber == -1) {
|
||||
if (g_c->invite_callback)
|
||||
g_c->invite_callback(m, friendnumber, invite_data, invite_length, g_c->invite_callback_userdata);
|
||||
|
||||
return;
|
||||
} else {
|
||||
//TODO
|
||||
uint16_t other_groupnum;
|
||||
memcpy(&other_groupnum, data + 1, sizeof(uint16_t));
|
||||
other_groupnum = ntohs(other_groupnum);
|
||||
add_friend_to_groupchat(g_c, friendnumber, groupnumber, other_groupnum);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case INVITE_RESPONSE_ID: {
|
||||
if (length != INVITE_RESPONSE_PACKET_SIZE)
|
||||
return;
|
||||
|
||||
uint16_t other_groupnum, groupnum;
|
||||
memcpy(&groupnum, data + 1 + sizeof(uint16_t), sizeof(uint16_t));
|
||||
groupnum = ntohs(groupnum);
|
||||
|
||||
Group_c *g = get_group_c(g_c, groupnum);
|
||||
|
||||
if (!g)
|
||||
return;
|
||||
|
||||
if (memcmp(data + 1 + sizeof(uint16_t) * 2, g->identifier, GROUP_IDENTIFIER_LENGTH) != 0)
|
||||
return;
|
||||
|
||||
memcpy(&other_groupnum, data + 1, sizeof(uint16_t));
|
||||
other_groupnum = ntohs(other_groupnum);
|
||||
|
||||
add_friend_to_groupchat(g_c, friendnumber, groupnum, other_groupnum);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find index of friend in the close list;
|
||||
*
|
||||
* returns index on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
static int friend_in_close(Group_c *g, int32_t friendnumber)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
|
||||
if (g->close[i].type != GROUPCHAT_CLOSE_CONNECTION)
|
||||
continue;
|
||||
|
||||
if (g->close[i].number != (uint32_t)friendnumber)
|
||||
continue;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define MIN_MESSAGE_PACKET_LEN (sizeof(uint16_t) * 2 + sizeof(uint32_t) + 1)
|
||||
|
||||
/* Send message to all close except receiver (if receiver isn't -1)
|
||||
* NOTE: this function appends the group chat number to the data passed to it.
|
||||
*
|
||||
* return number of messages sent.
|
||||
*/
|
||||
static unsigned int send_message_all_close(const Group_Chats *g_c, int groupnumber, const uint8_t *data,
|
||||
uint16_t length, int receiver)
|
||||
{
|
||||
|
||||
Group_c *g = get_group_c(g_c, groupnumber);
|
||||
|
||||
if (!g)
|
||||
return 0;
|
||||
|
||||
uint16_t i, sent = 0;
|
||||
|
||||
for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
|
||||
if (g->close[i].type == GROUPCHAT_CLOSE_NONE)
|
||||
continue;
|
||||
|
||||
if ((int)i == receiver)
|
||||
continue;
|
||||
|
||||
uint16_t other_groupnum = htons(g->close[i].group_number);
|
||||
uint8_t packet[sizeof(uint16_t) + length];
|
||||
memcpy(packet, &other_groupnum, sizeof(uint16_t));
|
||||
memcpy(packet + sizeof(uint16_t), data, length);
|
||||
|
||||
if (send_group_message_packet(g_c->m, g->close[i].number, packet, sizeof(packet)))
|
||||
++sent;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
#define MAX_GROUP_MESSAGE_DATA_LEN (MAX_CRYPTO_DATA_SIZE - (1 + MIN_MESSAGE_PACKET_LEN))
|
||||
|
||||
/* Send data of len with message_id to groupnumber.
|
||||
*
|
||||
* return number of peers it was sent to on success.
|
||||
* return 0 on failure.
|
||||
*/
|
||||
static unsigned int send_message_group(const Group_Chats *g_c, int groupnumber, uint8_t message_id, const uint8_t *data,
|
||||
uint16_t len)
|
||||
{
|
||||
if (len > MAX_GROUP_MESSAGE_DATA_LEN)
|
||||
return 0;
|
||||
|
||||
Group_c *g = get_group_c(g_c, groupnumber);
|
||||
|
||||
if (!g)
|
||||
return 0;
|
||||
|
||||
uint8_t packet[sizeof(uint16_t) + sizeof(uint32_t) + 1 + len];
|
||||
uint16_t peer_num = htons(g->peer_number);
|
||||
memcpy(packet, &peer_num, sizeof(peer_num));
|
||||
|
||||
++g->message_number;
|
||||
|
||||
if (!g->message_number)
|
||||
++g->message_number;
|
||||
|
||||
uint32_t message_num = htonl(g->message_number);
|
||||
memcpy(packet + sizeof(uint16_t), &message_num, sizeof(message_num));
|
||||
|
||||
packet[sizeof(uint16_t) + sizeof(uint32_t)] = message_id;
|
||||
|
||||
if (len)
|
||||
memcpy(packet + sizeof(uint16_t) + sizeof(uint32_t) + 1, data, len);
|
||||
|
||||
return send_message_all_close(g_c, groupnumber, packet, sizeof(packet), -1);
|
||||
}
|
||||
|
||||
/* send a group message
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_message_send(const Group_Chats *g_c, int groupnumber, const uint8_t *message, uint16_t length)
|
||||
{
|
||||
if (send_message_group(g_c, groupnumber, PACKET_ID_MESSAGE, message, length)) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_message_packet_group(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length,
|
||||
int close_index)
|
||||
{
|
||||
if (length < MIN_MESSAGE_PACKET_LEN)
|
||||
return;
|
||||
|
||||
Group_c *g = get_group_c(g_c, groupnumber);
|
||||
|
||||
if (!g)
|
||||
return;
|
||||
|
||||
uint16_t peer_number;
|
||||
memcpy(&peer_number, data + sizeof(uint16_t), sizeof(uint16_t));
|
||||
peer_number = ntohs(peer_number);
|
||||
|
||||
int index = get_peer_index(g, peer_number);
|
||||
|
||||
//TODO remove
|
||||
if (index == -1) {
|
||||
uint8_t empty_key[crypto_box_PUBLICKEYBYTES];
|
||||
index = addpeer(g, empty_key, peer_number);
|
||||
}
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
uint32_t message_number;
|
||||
memcpy(&message_number, data + sizeof(uint16_t) * 2, sizeof(message_number));
|
||||
message_number = ntohl(message_number);
|
||||
|
||||
if (g->group[index].last_message_number == 0) {
|
||||
g->group[index].last_message_number = message_number;
|
||||
} else if (message_number - g->group[index].last_message_number > 64 ||
|
||||
message_number == g->group[index].last_message_number) {
|
||||
return;
|
||||
}
|
||||
|
||||
g->group[index].last_message_number = message_number;
|
||||
|
||||
uint8_t message_id = data[sizeof(uint16_t) * 2 + sizeof(message_number)];
|
||||
const uint8_t *msg_data = data + sizeof(uint16_t) * 2 + sizeof(message_number) + 1;
|
||||
uint16_t msg_data_len = length - (sizeof(uint16_t) * 2 + sizeof(message_number) + 1);
|
||||
|
||||
switch (message_id) {
|
||||
case PACKET_ID_MESSAGE: {
|
||||
if (msg_data_len == 0)
|
||||
return;
|
||||
|
||||
//TODO
|
||||
if (g_c->message_callback)
|
||||
g_c->message_callback(g_c->m, groupnumber, index, msg_data, msg_data_len, g_c->message_callback_userdata);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
send_message_all_close(g_c, groupnumber, data + sizeof(uint16_t), length - sizeof(uint16_t), close_index);
|
||||
}
|
||||
|
||||
static void handle_friend_message_packet(Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
Group_Chats *g_c = m->group_chat_object;
|
||||
|
||||
if (length < MIN_MESSAGE_PACKET_LEN)
|
||||
return;
|
||||
|
||||
uint16_t groupnumber;
|
||||
memcpy(&groupnumber, data, sizeof(uint16_t));
|
||||
groupnumber = ntohs(groupnumber);
|
||||
Group_c *g = get_group_c(g_c, groupnumber);
|
||||
|
||||
if (!g)
|
||||
return;
|
||||
|
||||
int index = friend_in_close(g, friendnumber);
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
handle_message_packet_group(g_c, groupnumber, data, length, index);
|
||||
}
|
||||
|
||||
static int handle_packet(void *object, int number, uint8_t *data, uint16_t length)
|
||||
{
|
||||
if (length <= 1)
|
||||
return -1;
|
||||
|
||||
switch (data[0]) {
|
||||
case PACKET_ID_INVITE_GROUPCHAT: {
|
||||
handle_friend_invite_packet(object, number, data + 1, length - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case PACKET_ID_MESSAGE_GROUPCHAT: {
|
||||
handle_friend_message_packet(object, number, data + 1, length - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Create new groupchat instance. */
|
||||
Group_Chats *new_groupchats(Messenger *m)
|
||||
{
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
Group_Chats *temp = calloc(1, sizeof(Group_Chats));
|
||||
|
||||
if (temp == NULL)
|
||||
return NULL;
|
||||
|
||||
temp->m = m;
|
||||
m->group_chat_object = temp;
|
||||
m_callback_group_invite(m, &handle_friend_invite_packet);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* main groupchats loop. */
|
||||
void do_groupchats(Group_Chats *g_c)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
/* Free everything related with group chats. */
|
||||
void kill_groupchats(Group_Chats *g_c)
|
||||
{
|
||||
//TODO
|
||||
g_c->m->group_chat_object = 0;
|
||||
free(g_c);
|
||||
}
|
||||
|
||||
/* Return the number of chats in the instance m.
|
||||
* You should use this to determine how much memory to allocate
|
||||
* for copy_chatlist. */
|
||||
/*
|
||||
uint32_t count_chatlist(const Messenger *m)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < m->numchats; i++) {
|
||||
if (m->chats[i]) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
/* Copy a list of valid chat IDs into the array out_list.
|
||||
* If out_list is NULL, returns 0.
|
||||
* Otherwise, returns the number of elements copied.
|
||||
* If the array was too small, the contents
|
||||
* of out_list will be truncated to list_size. */
|
||||
/*
|
||||
uint32_t copy_chatlist(const Messenger *m, int *out_list, uint32_t list_size)
|
||||
{
|
||||
if (!out_list)
|
||||
return 0;
|
||||
|
||||
if (m->numchats == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t i;
|
||||
uint32_t ret = 0;
|
||||
|
||||
for (i = 0; i < m->numchats; i++) {
|
||||
if (ret >= list_size) {
|
||||
break; *//* Abandon ship *//*
|
||||
}
|
||||
|
||||
if (m->chats[i]) {
|
||||
out_list[ret] = i;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
*/
|
197
toxcore/group.h
Normal file
197
toxcore/group.h
Normal file
|
@ -0,0 +1,197 @@
|
|||
/* group.h
|
||||
*
|
||||
* Slightly better groupchats implementation.
|
||||
*
|
||||
* Copyright (C) 2014 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef GROUP_H
|
||||
#define GROUP_H
|
||||
|
||||
#include "Messenger.h"
|
||||
|
||||
enum {
|
||||
GROUPCHAT_STATUS_NONE,
|
||||
GROUPCHAT_STATUS_VALID
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||
uint64_t pingid;
|
||||
uint64_t last_pinged;
|
||||
|
||||
uint64_t last_recv;
|
||||
uint64_t last_recv_msgping;
|
||||
uint32_t last_message_number;
|
||||
|
||||
uint8_t nick[MAX_NAME_LENGTH];
|
||||
uint16_t nick_len;
|
||||
|
||||
uint8_t deleted;
|
||||
uint64_t deleted_time;
|
||||
|
||||
uint16_t peer_number;
|
||||
} Group_Peer;
|
||||
|
||||
#define DESIRED_CLOSE_CONNECTIONS 3
|
||||
#define MAX_GROUP_CONNECTIONS 16
|
||||
#define GROUP_IDENTIFIER_LENGTH crypto_box_KEYBYTES /* So we can use new_symmetric_key(...) to fill it */
|
||||
|
||||
enum {
|
||||
GROUPCHAT_CLOSE_NONE,
|
||||
GROUPCHAT_CLOSE_CONNECTION
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t status;
|
||||
|
||||
Group_Peer *group;
|
||||
uint32_t numpeers;
|
||||
|
||||
struct {
|
||||
uint8_t type; /* GROUPCHAT_CLOSE_* */
|
||||
uint32_t number;
|
||||
uint16_t group_number;
|
||||
} close[MAX_GROUP_CONNECTIONS];
|
||||
|
||||
uint8_t identifier[GROUP_IDENTIFIER_LENGTH];
|
||||
|
||||
uint32_t message_number;
|
||||
uint16_t peer_number;
|
||||
} Group_c;
|
||||
|
||||
typedef struct {
|
||||
Messenger *m;
|
||||
|
||||
Group_c *chats;
|
||||
uint32_t num_chats;
|
||||
|
||||
void (*invite_callback)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
|
||||
void *invite_callback_userdata;
|
||||
void (*message_callback)(Messenger *m, int, int, const uint8_t *, uint16_t, void *);
|
||||
void *message_callback_userdata;
|
||||
} Group_Chats;
|
||||
|
||||
/* Set the callback for group invites.
|
||||
*
|
||||
* Function(Group_Chats *g_c, int32_t friendnumber, uint8_t *data, uint16_t length, void *userdata)
|
||||
*
|
||||
* data of length is what needs to be passed to join_groupchat().
|
||||
*/
|
||||
void g_callback_group_invite(Group_Chats *g_c, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t,
|
||||
void *), void *userdata);
|
||||
|
||||
/* Set the callback for group messages.
|
||||
*
|
||||
* Function(Group_Chats *g_c, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata)
|
||||
*/
|
||||
void g_callback_group_message(Group_Chats *g_c, void (*function)(Messenger *m, int, int, const uint8_t *, uint16_t,
|
||||
void *), void *userdata);
|
||||
|
||||
/* Set the callback for group actions.
|
||||
*
|
||||
* Function(Group_Chats *g_c, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata)
|
||||
*/
|
||||
void g_callback_group_action(Group_Chats *g_c, void (*function)(Messenger *m, int, int, const uint8_t *, uint16_t,
|
||||
void *), void *userdata);
|
||||
|
||||
/* Set callback function for peer name list changes.
|
||||
*
|
||||
* It gets called every time the name list changes(new peer/name, deleted peer)
|
||||
* Function(Group_Chats *g_c, int groupnumber, void *userdata)
|
||||
*/
|
||||
void g_callback_group_namelistchange(Group_Chats *g_c, void (*function)(Messenger *m, int, int, uint8_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Creates a new groupchat and puts it in the chats array.
|
||||
*
|
||||
* return group number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_groupchat(Group_Chats *g_c);
|
||||
|
||||
/* Delete a groupchat from the chats array.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 if failure.
|
||||
*/
|
||||
int del_groupchat(Group_Chats *g_c, int groupnumber);
|
||||
|
||||
/* Copy the name of peernumber who is in groupnumber to name.
|
||||
* name must be at least MAX_NAME_LENGTH long.
|
||||
*
|
||||
* return length of name if success
|
||||
* return -1 if failure
|
||||
*/
|
||||
int group_peername(const Group_Chats *g_c, int groupnumber, int peernumber, uint8_t *name);
|
||||
|
||||
/* invite friendnumber to groupnumber
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int invite_friend(Group_Chats *g_c, int32_t friendnumber, int groupnumber);
|
||||
|
||||
/* Join a group (you need to have been invited first.)
|
||||
*
|
||||
* returns group number on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int join_groupchat(Group_Chats *g_c, int32_t friendnumber, const uint8_t *data, uint16_t length);
|
||||
|
||||
/* send a group message
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_message_send(const Group_Chats *g_c, int groupnumber, const uint8_t *message, uint16_t length);
|
||||
|
||||
/* send a group action
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_action_send(const Group_Chats *g_c, int groupnumber, const uint8_t *action, uint16_t length);
|
||||
|
||||
/* Return the number of peers in the group chat on success.
|
||||
* return -1 on failure
|
||||
*/
|
||||
int group_number_peers(const Group_Chats *g_c, int groupnumber);
|
||||
|
||||
/* List all the peers in the group chat.
|
||||
*
|
||||
* Copies the names of the peers to the name[length][MAX_NAME_LENGTH] array.
|
||||
*
|
||||
* Copies the lengths of the names to lengths[length]
|
||||
*
|
||||
* returns the number of peers on success.
|
||||
*
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int group_names(const Group_Chats *g_c, int groupnumber, uint8_t names[][MAX_NAME_LENGTH], uint16_t lengths[],
|
||||
uint16_t length);
|
||||
|
||||
/* Create new groupchat instance. */
|
||||
Group_Chats *new_groupchats(Messenger *m);
|
||||
|
||||
/* main groupchats loop. */
|
||||
void do_groupchats(Group_Chats *g_c);
|
||||
|
||||
/* Free everything related with group chats. */
|
||||
void kill_groupchats(Group_Chats *g_c);
|
||||
|
||||
#endif
|
|
@ -1,837 +0,0 @@
|
|||
/* group_chats.c
|
||||
*
|
||||
* An implementation of massive text only group chats.
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* 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
|
||||
|
||||
#include "DHT.h"
|
||||
#include "assoc.h"
|
||||
#include "group_chats.h"
|
||||
#include "LAN_discovery.h"
|
||||
#include "util.h"
|
||||
|
||||
#define GROUPCHAT_MAXDATA_LENGTH (MAX_CRYPTO_REQUEST_SIZE - (1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES))
|
||||
#define GROUPCHAT_MAXPLAINDATA_LENGTH (GROUPCHAT_MAXDATA_LENGTH - crypto_box_MACBYTES)
|
||||
|
||||
#define GROUP_MAX_SENDNODES (GROUP_CLOSE_CONNECTIONS * 2)
|
||||
|
||||
typedef struct {
|
||||
uint64_t pingid;
|
||||
//uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||
|
||||
} getnodes_data;
|
||||
|
||||
typedef struct {
|
||||
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||
IP_Port ip_port;
|
||||
|
||||
} groupchat_nodes;
|
||||
|
||||
typedef struct {
|
||||
uint64_t pingid;
|
||||
groupchat_nodes nodes[GROUP_CLOSE_CONNECTIONS];
|
||||
//uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||
|
||||
} sendnodes_data;
|
||||
|
||||
/*
|
||||
* check if peer with client_id is in peer array.
|
||||
*
|
||||
* return peer number if peer is in chat.
|
||||
* return -1 if peer is not in chat.
|
||||
*
|
||||
* TODO: make this more efficient.
|
||||
*/
|
||||
|
||||
static int peer_in_chat(const Group_Chat *chat, const uint8_t *client_id)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < chat->numpeers; ++i)
|
||||
if (id_equal(chat->group[i].client_id, client_id))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Compares client_id1 and client_id2 with client_id.
|
||||
*
|
||||
* return 0 if both are same distance.
|
||||
* return 1 if client_id1 is closer.
|
||||
* return 2 if client_id2 is closer.
|
||||
*/
|
||||
static int id_closest_groupchats(const uint8_t *id, const uint8_t *id1, const uint8_t *id2)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t distance1, distance2;
|
||||
|
||||
for (i = 0; i < CLIENT_ID_SIZE; ++i) {
|
||||
|
||||
distance1 = abs(((int8_t *)id)[i] - ((int8_t *)id1)[i]);
|
||||
distance2 = abs(((int8_t *)id)[i] - ((int8_t *)id2)[i]);
|
||||
|
||||
if (distance1 < distance2)
|
||||
return 1;
|
||||
|
||||
if (distance1 > distance2)
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BAD_GROUPNODE_TIMEOUT 30
|
||||
|
||||
/*
|
||||
* Check if peer is closer to us that the other peers in the list and if the peer is in the list.
|
||||
* Return the number of peers it is closer to if it is not in the closelist.
|
||||
* Return -1 if the peer is in the closelist.
|
||||
*/
|
||||
|
||||
static int peer_okping(const Group_Chat *chat, const uint8_t *client_id)
|
||||
{
|
||||
uint32_t i, j = 0;
|
||||
|
||||
if (id_equal(chat->self_public_key, client_id))
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
|
||||
if (is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
|
||||
++j;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Equal */
|
||||
if (id_equal(chat->close[i].client_id, client_id))
|
||||
return -1;
|
||||
|
||||
if (id_closest_groupchats(chat->self_public_key, chat->close[i].client_id, client_id) == 2)
|
||||
++j;
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Attempt to add a peer to the close list.
|
||||
* Update last_recv if it is in list.
|
||||
* Attempt to add it to list if it is not.
|
||||
*
|
||||
* Return 0 if success.
|
||||
* Return -1 if peer was not put in list/updated.
|
||||
*/
|
||||
static int add_closepeer(Group_Chat *chat, const uint8_t *client_id, IP_Port ip_port)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Check if node is already in list, if it is update its last_recv */
|
||||
if (id_equal(chat->close[i].client_id, client_id)) {
|
||||
chat->close[i].last_recv = unix_time();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Try replacing bad nodes first */
|
||||
if (is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
|
||||
id_copy(chat->close[i].client_id, client_id);
|
||||
chat->close[i].ip_port = ip_port;
|
||||
chat->close[i].last_recv = unix_time();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Replace nodes if given one is closer. */
|
||||
if (id_closest_groupchats(chat->self_public_key, chat->close[i].client_id, client_id) == 2) {
|
||||
id_copy(chat->close[i].client_id, client_id);
|
||||
chat->close[i].ip_port = ip_port;
|
||||
chat->close[i].last_recv = unix_time();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int send_groupchatpacket(const Group_Chat *chat, IP_Port ip_port, const uint8_t *public_key, const uint8_t *data,
|
||||
uint32_t length, uint8_t request_id)
|
||||
{
|
||||
if (id_equal(chat->self_public_key, public_key))
|
||||
return -1;
|
||||
|
||||
uint8_t packet[MAX_CRYPTO_REQUEST_SIZE];
|
||||
int len = create_request(chat->self_public_key, chat->self_secret_key, packet, public_key, data, length, request_id);
|
||||
packet[0] = NET_PACKET_GROUP_CHATS;
|
||||
|
||||
if (len == -1)
|
||||
return -1;
|
||||
|
||||
if (sendpacket(chat->net, ip_port, packet, len) == len)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Send data to all peers in close peer list.
|
||||
*
|
||||
* return the number of peers the packet was sent to.
|
||||
*/
|
||||
static uint8_t sendto_allpeers(const Group_Chat *chat, const uint8_t *data, uint16_t length, uint8_t request_id)
|
||||
{
|
||||
uint16_t sent = 0;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
|
||||
if (ip_isset(&chat->close[i].ip_port.ip) &&
|
||||
!is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
|
||||
if (send_groupchatpacket(chat, chat->close[i].ip_port, chat->close[i].client_id,
|
||||
data, length, request_id) == 0)
|
||||
++sent;
|
||||
}
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add a peer to the group chat.
|
||||
*
|
||||
* return peernum if success or peer already in chat.
|
||||
* return -1 if error.
|
||||
*/
|
||||
static int addpeer(Group_Chat *chat, const uint8_t *client_id)
|
||||
{
|
||||
int peernum = peer_in_chat(chat, client_id);
|
||||
|
||||
if (peernum != -1)
|
||||
return peernum;
|
||||
|
||||
Group_Peer *temp;
|
||||
temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers + 1));
|
||||
|
||||
if (temp == NULL)
|
||||
return -1;
|
||||
|
||||
memset(&(temp[chat->numpeers]), 0, sizeof(Group_Peer));
|
||||
chat->group = temp;
|
||||
|
||||
id_copy(chat->group[chat->numpeers].client_id, client_id);
|
||||
chat->group[chat->numpeers].last_recv = unix_time();
|
||||
chat->group[chat->numpeers].last_recv_msgping = unix_time();
|
||||
++chat->numpeers;
|
||||
|
||||
if (chat->peer_namelistchange != NULL)
|
||||
(*chat->peer_namelistchange)(chat, chat->numpeers - 1, CHAT_CHANGE_PEER_ADD, chat->group_namelistchange_userdata);
|
||||
|
||||
return (chat->numpeers - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a peer from the group chat to deleted.
|
||||
*
|
||||
* return 0 if success
|
||||
* return -1 if error.
|
||||
*/
|
||||
static int del_peer_set(Group_Chat *chat, int peernum)
|
||||
{
|
||||
if ((uint32_t)peernum >= chat->numpeers)
|
||||
return -1;
|
||||
|
||||
chat->group[peernum].deleted = 1;
|
||||
chat->group[peernum].deleted_time = unix_time();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a peer from the group chat.
|
||||
*
|
||||
* return 0 if success
|
||||
* return -1 if error.
|
||||
*/
|
||||
static int delpeer(Group_Chat *chat, int peernum)
|
||||
{
|
||||
if ((uint32_t)peernum >= chat->numpeers)
|
||||
return -1;
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* If peer is in close list, time it out forcefully. */
|
||||
if (id_equal(chat->close[i].client_id, chat->group[peernum].client_id)) {
|
||||
chat->close[i].last_recv = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Group_Peer *temp;
|
||||
--chat->numpeers;
|
||||
|
||||
if (chat->numpeers == 0) {
|
||||
free(chat->group);
|
||||
chat->group = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (chat->numpeers != (uint32_t)peernum)
|
||||
memcpy(&chat->group[peernum], &chat->group[chat->numpeers], sizeof(Group_Peer));
|
||||
|
||||
temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers));
|
||||
|
||||
if (temp == NULL)
|
||||
return -1;
|
||||
|
||||
chat->group = temp;
|
||||
|
||||
if (chat->peer_namelistchange != NULL) {
|
||||
(*chat->peer_namelistchange)(chat, peernum, CHAT_CHANGE_PEER_DEL, chat->group_namelistchange_userdata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy the name of peernum to name.
|
||||
* name must be at least MAX_NICK_BYTES long.
|
||||
*
|
||||
* return length of name if success
|
||||
* return -1 if failure
|
||||
*/
|
||||
int group_peername(const Group_Chat *chat, int peernum, uint8_t *name)
|
||||
{
|
||||
if ((uint32_t)peernum >= chat->numpeers)
|
||||
return -1;
|
||||
|
||||
if (chat->group[peernum].nick_len == 0) {
|
||||
/* memcpy(name, "NSA agent", 10); */ /* Srsly? */ /* Kindly remind the user that someone with no name might be a moronic NSA agent.*/
|
||||
name[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(name, chat->group[peernum].nick, chat->group[peernum].nick_len);
|
||||
return chat->group[peernum].nick_len;
|
||||
}
|
||||
|
||||
static void setnick(Group_Chat *chat, int peernum, const uint8_t *contents, uint16_t contents_len)
|
||||
{
|
||||
if (contents_len > MAX_NICK_BYTES || contents_len == 0)
|
||||
return;
|
||||
|
||||
/* same name as already stored? */
|
||||
if (chat->group[peernum].nick_len == contents_len)
|
||||
if (!memcmp(chat->group[peernum].nick, contents, contents_len))
|
||||
return;
|
||||
|
||||
memcpy(chat->group[peernum].nick, contents, contents_len);
|
||||
chat->group[peernum].nick_len = contents_len;
|
||||
|
||||
if (chat->peer_namelistchange != NULL)
|
||||
(*chat->peer_namelistchange)(chat, peernum, CHAT_CHANGE_PEER_NAME, chat->group_namelistchange_userdata);
|
||||
}
|
||||
|
||||
/* min time between pings sent to one peer in seconds */
|
||||
/* TODO: move this to global section */
|
||||
#define GROUP_PING_TIMEOUT 5
|
||||
|
||||
static int send_getnodes(const Group_Chat *chat, IP_Port ip_port, int peernum)
|
||||
{
|
||||
if ((uint32_t)peernum >= chat->numpeers)
|
||||
return -1;
|
||||
|
||||
if (!is_timeout(chat->group[peernum].last_pinged, GROUP_PING_TIMEOUT))
|
||||
return -1;
|
||||
|
||||
getnodes_data contents;
|
||||
contents.pingid = random_64b();
|
||||
|
||||
chat->group[peernum].last_pinged = unix_time();
|
||||
chat->group[peernum].pingid = contents.pingid;
|
||||
chat->group[peernum].ping_via = ip_port;
|
||||
|
||||
if (chat->assoc) {
|
||||
IPPTs ippts;
|
||||
ippts.timestamp = unix_time();
|
||||
ippts.ip_port = ip_port;
|
||||
|
||||
Assoc_add_entry(chat->assoc, chat->group[peernum].client_id, &ippts, NULL, 1);
|
||||
}
|
||||
|
||||
return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents, sizeof(contents),
|
||||
CRYPTO_PACKET_GROUP_CHAT_GET_NODES);
|
||||
}
|
||||
|
||||
static int send_sendnodes(const Group_Chat *chat, IP_Port ip_port, int peernum, uint64_t pingid)
|
||||
{
|
||||
if ((uint32_t)peernum >= chat->numpeers)
|
||||
return -1;
|
||||
|
||||
sendnodes_data contents;
|
||||
contents.pingid = pingid;
|
||||
uint32_t i, j = 0;
|
||||
|
||||
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
|
||||
if (!is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
|
||||
id_copy(contents.nodes[j].client_id, chat->close[i].client_id);
|
||||
contents.nodes[j].ip_port = chat->close[i].ip_port;
|
||||
to_net_family(&contents.nodes[j].ip_port.ip);
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents,
|
||||
sizeof(contents.pingid) + sizeof(groupchat_nodes) * j, CRYPTO_PACKET_GROUP_CHAT_SEND_NODES);
|
||||
}
|
||||
|
||||
static int handle_getnodes(const Group_Chat *chat, IP_Port source, int peernum, const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len != sizeof(getnodes_data))
|
||||
return 1;
|
||||
|
||||
if ((uint32_t)peernum >= chat->numpeers)
|
||||
return 1;
|
||||
|
||||
getnodes_data contents;
|
||||
memcpy(&contents, data, sizeof(contents));
|
||||
send_sendnodes(chat, source, peernum, contents.pingid);
|
||||
|
||||
if (peer_okping(chat, chat->group[peernum].client_id) > 0)
|
||||
send_getnodes(chat, source, peernum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_sendnodes(Group_Chat *chat, IP_Port source, int peernum, const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if ((uint32_t)peernum >= chat->numpeers)
|
||||
return 1;
|
||||
|
||||
if (len > sizeof(sendnodes_data) || len < sizeof(uint64_t))
|
||||
return 1;
|
||||
|
||||
if ((len - sizeof(uint64_t)) % sizeof(groupchat_nodes) != 0)
|
||||
return 1;
|
||||
|
||||
if (is_timeout(chat->group[peernum].last_pinged, GROUP_PING_TIMEOUT))
|
||||
return 1;
|
||||
|
||||
sendnodes_data contents;
|
||||
memcpy(&contents, data, len);
|
||||
|
||||
if (contents.pingid != chat->group[peernum].pingid)
|
||||
return 1;
|
||||
|
||||
uint16_t numnodes = (len - sizeof(contents.pingid)) / sizeof(groupchat_nodes);
|
||||
uint32_t i;
|
||||
|
||||
IPPTs ippts_send;
|
||||
ippts_send.timestamp = unix_time();
|
||||
|
||||
for (i = 0; i < numnodes; ++i) {
|
||||
if (peer_okping(chat, contents.nodes[i].client_id) > 0) {
|
||||
int peern = peer_in_chat(chat, contents.nodes[i].client_id);
|
||||
|
||||
if (peern == -1) { /*NOTE: This is just for testing and will be removed later.*/
|
||||
peern = addpeer(chat, contents.nodes[i].client_id);
|
||||
}
|
||||
|
||||
if (peern == -1)
|
||||
continue;
|
||||
|
||||
to_host_family(&contents.nodes[i].ip_port.ip);
|
||||
send_getnodes(chat, contents.nodes[i].ip_port, peern);
|
||||
|
||||
if (chat->assoc) {
|
||||
ippts_send.ip_port = contents.nodes[i].ip_port;
|
||||
Assoc_add_entry(chat->assoc, contents.nodes[i].client_id, &ippts_send, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_closepeer(chat, chat->group[peernum].client_id, source);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define GROUP_DATA_MIN_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + 1)
|
||||
static void send_names_new_peer(Group_Chat *chat);
|
||||
|
||||
static int handle_data(Group_Chat *chat, const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (len < GROUP_DATA_MIN_SIZE)
|
||||
return 1;
|
||||
|
||||
//TODO:
|
||||
int peernum = peer_in_chat(chat, data);
|
||||
|
||||
if (peernum == -1) { /*NOTE: This is just for testing and will be removed later.*/
|
||||
if (data[crypto_box_PUBLICKEYBYTES + sizeof(uint32_t)] != GROUP_CHAT_QUIT)
|
||||
peernum = addpeer(chat, data);
|
||||
}
|
||||
|
||||
if (peernum == -1)
|
||||
return 1;
|
||||
|
||||
if (chat->group[peernum].deleted)
|
||||
return 1;
|
||||
|
||||
/* Spam prevention (1 message per peer per second limit.)
|
||||
|
||||
if (chat->group[peernum].last_recv == temp_time)
|
||||
return 1;
|
||||
*/
|
||||
chat->group[peernum].last_recv = unix_time();
|
||||
|
||||
uint32_t message_num;
|
||||
memcpy(&message_num, data + crypto_box_PUBLICKEYBYTES, sizeof(uint32_t));
|
||||
message_num = ntohl(message_num);
|
||||
|
||||
if (chat->group[peernum].last_message_number == 0) {
|
||||
chat->group[peernum].last_message_number = message_num;
|
||||
} else if (message_num - chat->group[peernum].last_message_number > 64 ||
|
||||
message_num == chat->group[peernum].last_message_number)
|
||||
return 1;
|
||||
|
||||
chat->group[peernum].last_message_number = message_num;
|
||||
|
||||
int handled = 1;
|
||||
const uint8_t *contents = data + GROUP_DATA_MIN_SIZE;
|
||||
uint16_t contents_len = len - GROUP_DATA_MIN_SIZE;
|
||||
|
||||
switch (data[crypto_box_PUBLICKEYBYTES + sizeof(message_num)]) {
|
||||
case GROUP_CHAT_PING: /* If message is ping */
|
||||
if (contents_len != 0)
|
||||
return 1;
|
||||
|
||||
chat->group[peernum].last_recv_msgping = unix_time();
|
||||
break;
|
||||
|
||||
case GROUP_CHAT_NEW_PEER: /* If message is new peer */
|
||||
if (contents_len != crypto_box_PUBLICKEYBYTES)
|
||||
return 1;
|
||||
|
||||
addpeer(chat, contents);
|
||||
send_names_new_peer(chat);
|
||||
break;
|
||||
|
||||
case GROUP_CHAT_QUIT: /* If peer tells us he is quitting */
|
||||
if (contents_len != 0)
|
||||
return 1;
|
||||
|
||||
del_peer_set(chat, peernum);
|
||||
break;
|
||||
|
||||
case GROUP_CHAT_PEER_NICK:
|
||||
if (contents_len > MAX_NICK_BYTES || contents_len == 0)
|
||||
return 1;
|
||||
|
||||
setnick(chat, peernum, contents, contents_len);
|
||||
break;
|
||||
|
||||
case GROUP_CHAT_CHAT_MESSAGE: /* If message is chat message */
|
||||
if (chat->group_message != NULL)
|
||||
(*chat->group_message)(chat, peernum, contents, contents_len, chat->group_message_userdata);
|
||||
|
||||
break;
|
||||
|
||||
case GROUP_CHAT_ACTION: /* if message is a peer action */
|
||||
if (chat->group_action != NULL)
|
||||
(*chat->group_action)(chat, peernum, contents, contents_len, chat->group_action_userdata);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
handled = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (handled == 1) {
|
||||
sendto_allpeers(chat, data, len, CRYPTO_PACKET_GROUP_CHAT_BROADCAST);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t send_data(Group_Chat *chat, const uint8_t *data, uint32_t len, uint8_t message_id)
|
||||
{
|
||||
if (len + GROUP_DATA_MIN_SIZE > MAX_CRYPTO_REQUEST_SIZE) /*NOTE: not the real maximum len.*/
|
||||
return 1;
|
||||
|
||||
uint8_t packet[MAX_CRYPTO_REQUEST_SIZE];
|
||||
++chat->message_number;
|
||||
|
||||
if (chat->message_number == 0)
|
||||
chat->message_number = 1;
|
||||
|
||||
uint32_t message_num = htonl(chat->message_number);
|
||||
//TODO
|
||||
id_copy(packet, chat->self_public_key);
|
||||
memcpy(packet + crypto_box_PUBLICKEYBYTES, &message_num, sizeof(message_num));
|
||||
|
||||
if (len != 0)
|
||||
memcpy(packet + GROUP_DATA_MIN_SIZE, data, len);
|
||||
|
||||
packet[crypto_box_PUBLICKEYBYTES + sizeof(message_num)] = message_id;
|
||||
return sendto_allpeers(chat, packet, len + GROUP_DATA_MIN_SIZE, CRYPTO_PACKET_GROUP_CHAT_BROADCAST);
|
||||
}
|
||||
/*
|
||||
* Handle get nodes group packet.
|
||||
*
|
||||
* return 0 if handled correctly.
|
||||
* return 1 if error.
|
||||
*/
|
||||
|
||||
int handle_groupchatpacket(Group_Chat *chat, IP_Port source, const uint8_t *packet, uint32_t length)
|
||||
{
|
||||
if (length > MAX_CRYPTO_REQUEST_SIZE)
|
||||
return 1;
|
||||
|
||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t data[MAX_CRYPTO_REQUEST_SIZE];
|
||||
uint8_t number;
|
||||
int len = handle_request(chat->self_public_key, chat->self_secret_key, public_key, data, &number, packet, length);
|
||||
|
||||
if (len <= 0)
|
||||
return 1;
|
||||
|
||||
if (id_equal(chat->self_public_key, public_key))
|
||||
return 1;
|
||||
|
||||
int peernum = peer_in_chat(chat, public_key);
|
||||
|
||||
if (peernum == -1)
|
||||
return 1;
|
||||
|
||||
switch (number) {
|
||||
case CRYPTO_PACKET_GROUP_CHAT_GET_NODES:
|
||||
return handle_getnodes(chat, source, peernum, data, len);
|
||||
|
||||
case CRYPTO_PACKET_GROUP_CHAT_SEND_NODES:
|
||||
return handle_sendnodes(chat, source, peernum, data, len);
|
||||
|
||||
case CRYPTO_PACKET_GROUP_CHAT_BROADCAST:
|
||||
return handle_data(chat, data, len);
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t group_sendmessage(Group_Chat *chat, const uint8_t *message, uint32_t length)
|
||||
{
|
||||
return send_data(chat, message, length, GROUP_CHAT_CHAT_MESSAGE); //TODO: better return values?
|
||||
}
|
||||
|
||||
uint32_t group_sendaction(Group_Chat *chat, const uint8_t *action, uint32_t length)
|
||||
{
|
||||
return send_data(chat, action, length, GROUP_CHAT_ACTION);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send id/nick combo to the group.
|
||||
*
|
||||
* returns the number of peers it has sent it to.
|
||||
*/
|
||||
static uint32_t group_send_nick(Group_Chat *chat, uint8_t *nick, uint16_t nick_len)
|
||||
{
|
||||
if (nick_len > MAX_NICK_BYTES)
|
||||
return 0;
|
||||
|
||||
return send_data(chat, nick, nick_len, GROUP_CHAT_PEER_NICK);
|
||||
}
|
||||
|
||||
int set_nick(Group_Chat *chat, const uint8_t *nick, uint16_t nick_len)
|
||||
{
|
||||
if (nick_len > MAX_NICK_BYTES || nick_len == 0)
|
||||
return -1;
|
||||
|
||||
memcpy(chat->nick, nick, nick_len);
|
||||
chat->nick_len = nick_len;
|
||||
group_send_nick(chat, chat->nick, chat->nick_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t group_newpeer(Group_Chat *chat, const uint8_t *client_id)
|
||||
{
|
||||
addpeer(chat, client_id);
|
||||
return send_data(chat, client_id, crypto_box_PUBLICKEYBYTES, GROUP_CHAT_NEW_PEER); //TODO: better return values?
|
||||
}
|
||||
|
||||
void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata)
|
||||
{
|
||||
chat->group_message = function;
|
||||
chat->group_message_userdata = userdata;
|
||||
}
|
||||
|
||||
void callback_groupaction(Group_Chat *chat, void (*function)(Group_Chat *chat, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata)
|
||||
{
|
||||
chat->group_action = function;
|
||||
chat->group_action_userdata = userdata;
|
||||
}
|
||||
|
||||
void callback_namelistchange(Group_Chat *chat, void (*function)(Group_Chat *chat, int peer, uint8_t change, void *),
|
||||
void *userdata)
|
||||
{
|
||||
chat->peer_namelistchange = function;
|
||||
chat->group_namelistchange_userdata = userdata;
|
||||
}
|
||||
|
||||
uint32_t group_numpeers(const Group_Chat *chat)
|
||||
{
|
||||
return chat->numpeers;
|
||||
}
|
||||
|
||||
uint32_t group_client_names(const Group_Chat *chat, uint8_t names[][MAX_NICK_BYTES], uint16_t lengths[],
|
||||
uint16_t length)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < chat->numpeers && i < length; ++i) {
|
||||
lengths[i] = group_peername(chat, i, names[i]);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
Group_Chat *new_groupchat(Networking_Core *net)
|
||||
{
|
||||
unix_time_update();
|
||||
|
||||
if (net == 0)
|
||||
return 0;
|
||||
|
||||
Group_Chat *chat = calloc(1, sizeof(Group_Chat));
|
||||
chat->net = net;
|
||||
crypto_box_keypair(chat->self_public_key, chat->self_secret_key);
|
||||
|
||||
/* (2^4) * 5 = 80 entries seems to be a moderate size */
|
||||
chat->assoc = new_Assoc(4, 5, chat->self_public_key);
|
||||
|
||||
return chat;
|
||||
}
|
||||
|
||||
#define NODE_PING_INTERVAL 10
|
||||
|
||||
static void ping_close(Group_Chat *chat)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
|
||||
if (!is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
|
||||
int peernum = peer_in_chat(chat, chat->close[i].client_id);
|
||||
|
||||
if (peernum == -1)
|
||||
continue;
|
||||
|
||||
if (is_timeout(chat->group[peernum].last_pinged, NODE_PING_INTERVAL))
|
||||
send_getnodes(chat, chat->close[i].ip_port, peernum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Interval in seconds to send ping messages */
|
||||
#define GROUP_PING_INTERVAL 30
|
||||
|
||||
static void ping_group(Group_Chat *chat)
|
||||
{
|
||||
if (is_timeout(chat->last_sent_ping, GROUP_PING_INTERVAL)) {
|
||||
if (send_data(chat, 0, 0, GROUP_CHAT_PING) != 0) /* Ping */
|
||||
chat->last_sent_ping = unix_time();
|
||||
}
|
||||
}
|
||||
|
||||
#define DEL_PEER_DELAY 3
|
||||
static void del_dead_peers(Group_Chat *chat)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < chat->numpeers; ++i) {
|
||||
if (is_timeout(chat->group[i].last_recv_msgping, GROUP_PING_INTERVAL * 4)) {
|
||||
delpeer(chat, i);
|
||||
}
|
||||
|
||||
if (chat->group == NULL || i >= chat->numpeers)
|
||||
break;
|
||||
|
||||
if (chat->group[i].deleted) {
|
||||
if (is_timeout(chat->group[i].deleted_time, DEL_PEER_DELAY))
|
||||
delpeer(chat, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define NICK_SEND_INTERVAL 180
|
||||
static void send_names_new_peer(Group_Chat *chat)
|
||||
{
|
||||
group_send_nick(chat, chat->nick, chat->nick_len);
|
||||
chat->last_sent_nick = (unix_time() - NICK_SEND_INTERVAL) + 15;
|
||||
}
|
||||
static void send_names(Group_Chat *chat)
|
||||
{
|
||||
/* send own nick from time to time, to let newly added peers be informed
|
||||
* first time only: use a shorter timeframe, because we might not be in our own
|
||||
* peer list yet */
|
||||
if (is_timeout(chat->last_sent_nick, 180))
|
||||
if (group_send_nick(chat, chat->nick, chat->nick_len) > 0) {
|
||||
if (!chat->last_sent_nick)
|
||||
chat->last_sent_nick = (unix_time() - NICK_SEND_INTERVAL) + 10;
|
||||
else
|
||||
chat->last_sent_nick = unix_time();
|
||||
}
|
||||
}
|
||||
|
||||
void do_groupchat(Group_Chat *chat)
|
||||
{
|
||||
unix_time_update();
|
||||
ping_close(chat);
|
||||
ping_group(chat);
|
||||
/* TODO: Maybe run this less? */
|
||||
del_dead_peers(chat);
|
||||
send_names(chat);
|
||||
}
|
||||
|
||||
void kill_groupchat(Group_Chat *chat)
|
||||
{
|
||||
send_data(chat, 0, 0, GROUP_CHAT_QUIT);
|
||||
kill_Assoc(chat->assoc);
|
||||
free(chat->group);
|
||||
free(chat);
|
||||
}
|
||||
|
||||
void chat_bootstrap(Group_Chat *chat, IP_Port ip_port, const uint8_t *client_id)
|
||||
{
|
||||
send_getnodes(chat, ip_port, addpeer(chat, client_id));
|
||||
}
|
||||
|
||||
void chat_bootstrap_nonlazy(Group_Chat *chat, IP_Port ip_port, const uint8_t *client_id)
|
||||
{
|
||||
send_getnodes(chat, ip_port, addpeer(chat, client_id));
|
||||
add_closepeer(chat, client_id, ip_port);
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
/* group_chats.h
|
||||
*
|
||||
* An implementation of massive text only group chats.
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GROUP_CHATS_H
|
||||
#define GROUP_CHATS_H
|
||||
|
||||
#define MAX_NICK_BYTES 128
|
||||
|
||||
typedef struct {
|
||||
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||
uint64_t pingid;
|
||||
uint64_t last_pinged;
|
||||
IP_Port ping_via;
|
||||
|
||||
uint64_t last_recv;
|
||||
uint64_t last_recv_msgping;
|
||||
uint32_t last_message_number;
|
||||
|
||||
uint8_t nick[MAX_NICK_BYTES];
|
||||
uint16_t nick_len;
|
||||
|
||||
uint8_t deleted;
|
||||
uint64_t deleted_time;
|
||||
} Group_Peer;
|
||||
|
||||
typedef struct {
|
||||
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||
IP_Port ip_port;
|
||||
uint64_t last_recv;
|
||||
} Group_Close;
|
||||
|
||||
#define GROUP_CLOSE_CONNECTIONS 6
|
||||
|
||||
typedef struct Group_Chat {
|
||||
Networking_Core *net;
|
||||
uint8_t self_public_key[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t self_secret_key[crypto_box_SECRETKEYBYTES];
|
||||
|
||||
Group_Peer *group;
|
||||
Group_Close close[GROUP_CLOSE_CONNECTIONS];
|
||||
uint32_t numpeers;
|
||||
|
||||
uint32_t message_number;
|
||||
void (*group_message)(struct Group_Chat *m, int, const uint8_t *, uint16_t, void *);
|
||||
void *group_message_userdata;
|
||||
void (*group_action)(struct Group_Chat *m, int, const uint8_t *, uint16_t, void *);
|
||||
void *group_action_userdata;
|
||||
void (*peer_namelistchange)(struct Group_Chat *m, int peer, uint8_t change, void *);
|
||||
void *group_namelistchange_userdata;
|
||||
|
||||
uint64_t last_sent_ping;
|
||||
|
||||
uint8_t nick[MAX_NICK_BYTES];
|
||||
uint16_t nick_len;
|
||||
uint64_t last_sent_nick;
|
||||
|
||||
struct Assoc *assoc;
|
||||
} Group_Chat;
|
||||
|
||||
#define GROUP_CHAT_PING 0
|
||||
#define GROUP_CHAT_NEW_PEER 16
|
||||
#define GROUP_CHAT_QUIT 24
|
||||
#define GROUP_CHAT_PEER_NICK 48
|
||||
#define GROUP_CHAT_CHAT_MESSAGE 64
|
||||
#define GROUP_CHAT_ACTION 63
|
||||
|
||||
/* Copy the name of peernum to name.
|
||||
* name must be at least MAX_NICK_BYTES long.
|
||||
*
|
||||
* return length of name if success
|
||||
* return -1 if failure
|
||||
*/
|
||||
int group_peername(const Group_Chat *chat, int peernum, uint8_t *name);
|
||||
|
||||
/*
|
||||
* Set callback function for chat messages.
|
||||
*
|
||||
* format of function is: function(Group_Chat *chat, peer number, message, message length, userdata)
|
||||
*/
|
||||
void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/*
|
||||
* Set callback function for actions.
|
||||
*
|
||||
* format of function is: function(Group_Chat *chat, peer number, action, action length, userdata)
|
||||
*/
|
||||
void callback_groupaction(Group_Chat *chat, void (*function)(Group_Chat *chat, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/*
|
||||
* Set callback function for peer name list changes.
|
||||
*
|
||||
* It gets called every time the name list changes(new peer/name, deleted peer)
|
||||
*
|
||||
* format of function is: function(Group_Chat *chat, userdata)
|
||||
*/
|
||||
typedef enum {
|
||||
CHAT_CHANGE_PEER_ADD,
|
||||
CHAT_CHANGE_PEER_DEL,
|
||||
CHAT_CHANGE_PEER_NAME,
|
||||
} CHAT_CHANGE;
|
||||
|
||||
void callback_namelistchange(Group_Chat *chat, void (*function)(Group_Chat *chat, int peer, uint8_t change, void *),
|
||||
void *userdata);
|
||||
|
||||
/*
|
||||
* Send a message to the group.
|
||||
*
|
||||
* returns the number of peers it has sent it to.
|
||||
*/
|
||||
uint32_t group_sendmessage(Group_Chat *chat, const uint8_t *message, uint32_t length);
|
||||
|
||||
/*
|
||||
* Send an action to the group.
|
||||
*
|
||||
* returns the number of peers it has sent it to.
|
||||
*/
|
||||
uint32_t group_sendaction(Group_Chat *chat, const uint8_t *action, uint32_t length);
|
||||
|
||||
/*
|
||||
* Set our nick for this group.
|
||||
*
|
||||
* returns -1 on failure, 0 on success.
|
||||
*/
|
||||
int set_nick(Group_Chat *chat, const uint8_t *nick, uint16_t nick_len);
|
||||
|
||||
/*
|
||||
* Tell everyone about a new peer (a person we are inviting for example.)
|
||||
*
|
||||
*/
|
||||
uint32_t group_newpeer(Group_Chat *chat, const uint8_t *client_id);
|
||||
|
||||
|
||||
/* Create a new group chat.
|
||||
*
|
||||
* Returns a new group chat instance if success.
|
||||
*
|
||||
* Returns a NULL pointer if fail.
|
||||
*/
|
||||
Group_Chat *new_groupchat(Networking_Core *net);
|
||||
|
||||
|
||||
/* Return the number of peers in the group chat.
|
||||
*/
|
||||
uint32_t group_numpeers(const Group_Chat *chat);
|
||||
|
||||
/* List all the peers in the group chat.
|
||||
*
|
||||
* Copies the names of the peers to the name[length][MAX_NICK_BYTES] array.
|
||||
*
|
||||
* returns the number of peers.
|
||||
*/
|
||||
uint32_t group_client_names(const Group_Chat *chat, uint8_t names[][MAX_NICK_BYTES], uint16_t lengths[],
|
||||
uint16_t length);
|
||||
|
||||
/* Kill a group chat
|
||||
*
|
||||
* Frees the memory and everything.
|
||||
*/
|
||||
void kill_groupchat(Group_Chat *chat);
|
||||
|
||||
/*
|
||||
* This is the main loop.
|
||||
*/
|
||||
void do_groupchat(Group_Chat *chat);
|
||||
|
||||
/* if we receive a group chat packet we call this function so it can be handled.
|
||||
return 0 if packet is handled correctly.
|
||||
return 1 if it didn't handle the packet or if the packet was shit. */
|
||||
int handle_groupchatpacket(Group_Chat *chat, IP_Port source, const uint8_t *packet, uint32_t length);
|
||||
|
||||
|
||||
void chat_bootstrap(Group_Chat *chat, IP_Port ip_port, const uint8_t *client_id);
|
||||
void chat_bootstrap_nonlazy(Group_Chat *chat, IP_Port ip_port, const uint8_t *client_id);
|
||||
|
||||
|
||||
#endif
|
|
@ -1238,7 +1238,11 @@ static int handle_packet_connection(Net_Crypto *c, int crypt_connection_id, cons
|
|||
|
||||
conn->status = CRYPTO_CONN_NOT_CONFIRMED;
|
||||
/* Status needs to be CRYPTO_CONN_NOT_CONFIRMED for this to work. */
|
||||
set_connection_dht_public_key(c, crypt_connection_id, dht_public_key, current_time_monotonic());
|
||||
set_connection_dht_public_key(c, crypt_connection_id, dht_public_key);
|
||||
|
||||
if (conn->dht_pk_callback)
|
||||
conn->dht_pk_callback(conn->dht_pk_callback_object, conn->dht_pk_callback_number, dht_public_key);
|
||||
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
@ -1473,7 +1477,11 @@ static int handle_new_connection_handshake(Net_Crypto *c, IP_Port source, const
|
|||
if (create_send_handshake(c, crypt_connection_id, n_c.cookie, n_c.dht_public_key) == 0) {
|
||||
conn->status = CRYPTO_CONN_NOT_CONFIRMED;
|
||||
/* Status needs to be CRYPTO_CONN_NOT_CONFIRMED for this to work. */
|
||||
set_connection_dht_public_key(c, crypt_connection_id, n_c.dht_public_key, current_time_monotonic());
|
||||
set_connection_dht_public_key(c, crypt_connection_id, n_c.dht_public_key);
|
||||
|
||||
if (conn->dht_pk_callback)
|
||||
conn->dht_pk_callback(conn->dht_pk_callback_object, conn->dht_pk_callback_number, n_c.dht_public_key);
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
@ -1522,7 +1530,7 @@ int accept_crypto_connection(Net_Crypto *c, New_Connection *n_c)
|
|||
|
||||
conn->status = CRYPTO_CONN_NOT_CONFIRMED;
|
||||
/* Status needs to be CRYPTO_CONN_NOT_CONFIRMED for this to work. */
|
||||
set_connection_dht_public_key(c, crypt_connection_id, n_c->dht_public_key, current_time_monotonic());
|
||||
set_connection_dht_public_key(c, crypt_connection_id, n_c->dht_public_key);
|
||||
conn->packet_send_rate = CRYPTO_PACKET_MIN_RATE;
|
||||
conn->packets_left = CRYPTO_MIN_QUEUE_LENGTH;
|
||||
crypto_connection_add_source(c, crypt_connection_id, n_c->source);
|
||||
|
@ -1618,9 +1626,9 @@ static int connect_peer_tcp(Net_Crypto *c, int crypt_connection_id)
|
|||
/* Copy friends DHT public key into dht_key.
|
||||
*
|
||||
* return 0 on failure (no key copied).
|
||||
* return timestamp on success (key copied).
|
||||
* return 1 on success (key copied).
|
||||
*/
|
||||
uint64_t get_connection_dht_key(const Net_Crypto *c, int crypt_connection_id, uint8_t *dht_public_key)
|
||||
unsigned int get_connection_dht_key(const Net_Crypto *c, int crypt_connection_id, uint8_t *dht_public_key)
|
||||
{
|
||||
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
|
||||
|
||||
|
@ -1631,28 +1639,22 @@ uint64_t get_connection_dht_key(const Net_Crypto *c, int crypt_connection_id, ui
|
|||
return 0;
|
||||
|
||||
memcpy(dht_public_key, conn->dht_public_key, crypto_box_PUBLICKEYBYTES);
|
||||
return conn->dht_public_key_timestamp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Set the DHT public key of the crypto connection.
|
||||
* timestamp is the time (current_time_monotonic()) at which the key was last confirmed belonging to
|
||||
* the other peer.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int set_connection_dht_public_key(Net_Crypto *c, int crypt_connection_id, const uint8_t *dht_public_key,
|
||||
uint64_t timestamp)
|
||||
int set_connection_dht_public_key(Net_Crypto *c, int crypt_connection_id, const uint8_t *dht_public_key)
|
||||
{
|
||||
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
|
||||
|
||||
if (conn == 0)
|
||||
return -1;
|
||||
|
||||
if (timestamp <= conn->dht_public_key_timestamp)
|
||||
return -1;
|
||||
|
||||
if (conn->dht_public_key_set == 1 && memcmp(conn->dht_public_key, dht_public_key, crypto_box_PUBLICKEYBYTES) == 0)
|
||||
return -1;
|
||||
|
||||
|
@ -1662,7 +1664,6 @@ int set_connection_dht_public_key(Net_Crypto *c, int crypt_connection_id, const
|
|||
|
||||
memcpy(conn->dht_public_key, dht_public_key, crypto_box_PUBLICKEYBYTES);
|
||||
conn->dht_public_key_set = 1;
|
||||
conn->dht_public_key_timestamp = timestamp;
|
||||
|
||||
if (conn->status == CRYPTO_CONN_COOKIE_REQUESTING) {
|
||||
conn->cookie_request_number = random_64b();
|
||||
|
@ -1692,6 +1693,9 @@ int set_direct_ip_port(Net_Crypto *c, int crypt_connection_id, IP_Port ip_port)
|
|||
if (conn == 0)
|
||||
return -1;
|
||||
|
||||
if (ip_port.ip.family != AF_INET && ip_port.ip.family != AF_INET6)
|
||||
return -1;
|
||||
|
||||
if (!ipport_equal(&ip_port, &conn->ip_port)) {
|
||||
if (bs_list_add(&c->ip_port_list, &ip_port, crypt_connection_id)) {
|
||||
bs_list_remove(&c->ip_port_list, &conn->ip_port, crypt_connection_id);
|
||||
|
@ -2243,6 +2247,29 @@ int connection_lossy_data_handler(Net_Crypto *c, int crypt_connection_id,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Set the function for this friend that will be callbacked with object and number
|
||||
* when that friend gives us his DHT temporary public key.
|
||||
*
|
||||
* object and number will be passed as argument to this function.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int nc_dht_pk_callback(Net_Crypto *c, int crypt_connection_id, void (*function)(void *data, int32_t number,
|
||||
const uint8_t *dht_public_key), void *object, uint32_t number)
|
||||
{
|
||||
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
|
||||
|
||||
if (conn == 0)
|
||||
return -1;
|
||||
|
||||
conn->dht_pk_callback = function;
|
||||
conn->dht_pk_callback_object = object;
|
||||
conn->dht_pk_callback_number = number;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the crypto connection id from the ip_port.
|
||||
*
|
||||
* return -1 on failure.
|
||||
|
|
|
@ -111,7 +111,6 @@ typedef struct {
|
|||
uint64_t cookie_request_number; /* number used in the cookie request packets for this connection */
|
||||
uint8_t dht_public_key[crypto_box_PUBLICKEYBYTES]; /* The dht public key of the peer */
|
||||
uint8_t dht_public_key_set; /* True if the dht public key is set, false if it isn't. */
|
||||
uint64_t dht_public_key_timestamp; /* Timestamp of the last time we confirmed the key was correct. */
|
||||
|
||||
uint8_t *temp_packet; /* Where the cookie request/handshake packet is stored while it is being sent. */
|
||||
uint16_t temp_packet_length;
|
||||
|
@ -161,6 +160,10 @@ typedef struct {
|
|||
uint8_t maximum_speed_reached;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
void (*dht_pk_callback)(void *data, int32_t number, const uint8_t *dht_public_key);
|
||||
void *dht_pk_callback_object;
|
||||
uint32_t dht_pk_callback_number;
|
||||
} Crypto_Connection;
|
||||
|
||||
typedef struct {
|
||||
|
@ -236,19 +239,16 @@ int new_crypto_connection(Net_Crypto *c, const uint8_t *real_public_key);
|
|||
/* Copy friends DHT public key into dht_key.
|
||||
*
|
||||
* return 0 on failure (no key copied).
|
||||
* return timestamp on success (key copied).
|
||||
* return 1 on success (key copied).
|
||||
*/
|
||||
uint64_t get_connection_dht_key(const Net_Crypto *c, int crypt_connection_id, uint8_t *dht_public_key);
|
||||
unsigned int get_connection_dht_key(const Net_Crypto *c, int crypt_connection_id, uint8_t *dht_public_key);
|
||||
|
||||
/* Set the DHT public key of the crypto connection.
|
||||
* timestamp is the time (current_time_monotonic()) at which the key was last confirmed belonging to
|
||||
* the other peer.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int set_connection_dht_public_key(Net_Crypto *c, int crypt_connection_id, const uint8_t *dht_public_key,
|
||||
uint64_t timestamp);
|
||||
int set_connection_dht_public_key(Net_Crypto *c, int crypt_connection_id, const uint8_t *dht_public_key);
|
||||
|
||||
/* Set the direct ip of the crypto connection.
|
||||
*
|
||||
|
@ -294,6 +294,17 @@ int connection_lossy_data_handler(Net_Crypto *c, int crypt_connection_id,
|
|||
int (*connection_lossy_data_callback)(void *object, int id, const uint8_t *data, uint16_t length), void *object,
|
||||
int id);
|
||||
|
||||
/* Set the function for this friend that will be callbacked with object and number
|
||||
* when that friend gives us his DHT temporary public key.
|
||||
*
|
||||
* object and number will be passed as argument to this function.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int nc_dht_pk_callback(Net_Crypto *c, int crypt_connection_id, void (*function)(void *data, int32_t number,
|
||||
const uint8_t *dht_public_key), void *object, uint32_t number);
|
||||
|
||||
/* returns the number of packet slots left in the sendbuffer.
|
||||
* return 0 if failure.
|
||||
*/
|
||||
|
|
|
@ -105,7 +105,6 @@ typedef int sock_t;
|
|||
#define NET_PACKET_CRYPTO_DATA 27 /* Crypto data packet */
|
||||
#define NET_PACKET_CRYPTO 32 /* Encrypted data packet ID. */
|
||||
#define NET_PACKET_LAN_DISCOVERY 33 /* LAN discovery packet ID. */
|
||||
#define NET_PACKET_GROUP_CHATS 48 /* Group chats packet ID. */
|
||||
|
||||
/* See: docs/Prevent_Tracking.txt and onion.{c, h} */
|
||||
#define NET_PACKET_ONION_SEND_INITIAL 128
|
||||
|
@ -135,8 +134,7 @@ typedef int sock_t;
|
|||
#define TCP_INET6 (AF_INET6 + 3)
|
||||
#define TCP_FAMILY (AF_INET6 + 4)
|
||||
|
||||
typedef union __attribute__ ((__packed__))
|
||||
{
|
||||
typedef union {
|
||||
uint8_t uint8[4];
|
||||
uint16_t uint16[2];
|
||||
uint32_t uint32;
|
||||
|
@ -144,8 +142,7 @@ typedef union __attribute__ ((__packed__))
|
|||
}
|
||||
IP4;
|
||||
|
||||
typedef union __attribute__ ((__packed__))
|
||||
{
|
||||
typedef union {
|
||||
uint8_t uint8[16];
|
||||
uint16_t uint16[8];
|
||||
uint32_t uint32[4];
|
||||
|
@ -154,8 +151,7 @@ typedef union __attribute__ ((__packed__))
|
|||
}
|
||||
IP6;
|
||||
|
||||
typedef struct __attribute__ ((__packed__))
|
||||
{
|
||||
typedef struct {
|
||||
uint8_t family;
|
||||
union {
|
||||
IP4 ip4;
|
||||
|
@ -164,8 +160,7 @@ typedef struct __attribute__ ((__packed__))
|
|||
}
|
||||
IP;
|
||||
|
||||
typedef struct __attribute__ ((__packed__)) __attribute__((gcc_struct))
|
||||
{
|
||||
typedef struct {
|
||||
IP ip;
|
||||
uint16_t port;
|
||||
}
|
||||
|
|
|
@ -638,7 +638,12 @@ static int handle_fakeid_announce(void *object, const uint8_t *source_pubkey, co
|
|||
return 1;
|
||||
|
||||
onion_c->friends_list[friend_num].last_noreplay = no_replay;
|
||||
onion_set_friend_DHT_pubkey(onion_c, friend_num, data + 1 + sizeof(uint64_t), current_time_monotonic());
|
||||
|
||||
if (onion_c->friends_list[friend_num].dht_pk_callback)
|
||||
onion_c->friends_list[friend_num].dht_pk_callback(onion_c->friends_list[friend_num].dht_pk_callback_object,
|
||||
onion_c->friends_list[friend_num].dht_pk_callback_number, data + 1 + sizeof(uint64_t));
|
||||
|
||||
onion_set_friend_DHT_pubkey(onion_c, friend_num, data + 1 + sizeof(uint64_t));
|
||||
onion_c->friends_list[friend_num].last_seen = unix_time();
|
||||
|
||||
uint16_t len_nodes = length - FAKEID_DATA_MIN_LENGTH;
|
||||
|
@ -957,8 +962,8 @@ int onion_delfriend(Onion_Client *onion_c, int friend_num)
|
|||
if ((uint32_t)friend_num >= onion_c->num_friends)
|
||||
return -1;
|
||||
|
||||
if (onion_c->friends_list[friend_num].is_fake_clientid)
|
||||
DHT_delfriend(onion_c->dht, onion_c->friends_list[friend_num].fake_client_id);
|
||||
//if (onion_c->friends_list[friend_num].is_fake_clientid)
|
||||
// DHT_delfriend(onion_c->dht, onion_c->friends_list[friend_num].fake_client_id, 0);
|
||||
|
||||
memset(&(onion_c->friends_list[friend_num]), 0, sizeof(Onion_Friend));
|
||||
uint32_t i;
|
||||
|
@ -996,14 +1001,32 @@ int recv_tcp_relay_handler(Onion_Client *onion_c, int friend_num, int (*tcp_rela
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Set a friends DHT public key.
|
||||
* timestamp is the time (current_time_monotonic()) at which the key was last confirmed belonging to
|
||||
* the other peer.
|
||||
/* Set the function for this friend that will be callbacked with object and number
|
||||
* when that friend gives us his DHT temporary public key.
|
||||
*
|
||||
* object and number will be passed as argument to this function.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uint8_t *dht_key, uint64_t timestamp)
|
||||
int onion_dht_pk_callback(Onion_Client *onion_c, int friend_num, void (*function)(void *data, int32_t number,
|
||||
const uint8_t *dht_public_key), void *object, uint32_t number)
|
||||
{
|
||||
if ((uint32_t)friend_num >= onion_c->num_friends)
|
||||
return -1;
|
||||
|
||||
onion_c->friends_list[friend_num].dht_pk_callback = function;
|
||||
onion_c->friends_list[friend_num].dht_pk_callback_object = object;
|
||||
onion_c->friends_list[friend_num].dht_pk_callback_number = number;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set a friends DHT public key.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uint8_t *dht_key)
|
||||
{
|
||||
if ((uint32_t)friend_num >= onion_c->num_friends)
|
||||
return -1;
|
||||
|
@ -1011,24 +1034,16 @@ int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uin
|
|||
if (onion_c->friends_list[friend_num].status == 0)
|
||||
return -1;
|
||||
|
||||
if (onion_c->friends_list[friend_num].fake_client_id_timestamp >= timestamp)
|
||||
return -1;
|
||||
|
||||
if (onion_c->friends_list[friend_num].is_fake_clientid) {
|
||||
if (memcmp(dht_key, onion_c->friends_list[friend_num].fake_client_id, crypto_box_PUBLICKEYBYTES) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DHT_delfriend(onion_c->dht, onion_c->friends_list[friend_num].fake_client_id);
|
||||
}
|
||||
|
||||
if (DHT_addfriend(onion_c->dht, dht_key) == 1) {
|
||||
return -1;
|
||||
onion_c->friends_list[friend_num].is_fake_clientid = 0;
|
||||
}
|
||||
|
||||
onion_c->friends_list[friend_num].last_seen = unix_time();
|
||||
onion_c->friends_list[friend_num].is_fake_clientid = 1;
|
||||
onion_c->friends_list[friend_num].fake_client_id_timestamp = timestamp;
|
||||
memcpy(onion_c->friends_list[friend_num].fake_client_id, dht_key, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
return 0;
|
||||
|
@ -1037,9 +1052,9 @@ int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uin
|
|||
/* Copy friends DHT public key into dht_key.
|
||||
*
|
||||
* return 0 on failure (no key copied).
|
||||
* return timestamp on success (key copied).
|
||||
* return 1 on success (key copied).
|
||||
*/
|
||||
uint64_t onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_num, uint8_t *dht_key)
|
||||
unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_num, uint8_t *dht_key)
|
||||
{
|
||||
if ((uint32_t)friend_num >= onion_c->num_friends)
|
||||
return 0;
|
||||
|
@ -1051,7 +1066,7 @@ uint64_t onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_num,
|
|||
return 0;
|
||||
|
||||
memcpy(dht_key, onion_c->friends_list[friend_num].fake_client_id, crypto_box_PUBLICKEYBYTES);
|
||||
return onion_c->friends_list[friend_num].fake_client_id_timestamp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get the ip of friend friendnum and put it in ip_port
|
||||
|
@ -1189,23 +1204,6 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
|
|||
}
|
||||
}
|
||||
|
||||
/* Timeout before which a peer is considered dead and removed from the DHT search. */
|
||||
#define DEAD_ONION_TIMEOUT (10 * 60)
|
||||
|
||||
static void cleanup_friend(Onion_Client *onion_c, uint16_t friendnum)
|
||||
{
|
||||
if (friendnum >= onion_c->num_friends)
|
||||
return;
|
||||
|
||||
if (onion_c->friends_list[friendnum].status == 0)
|
||||
return;
|
||||
|
||||
if (onion_c->friends_list[friendnum].is_fake_clientid && !onion_c->friends_list[friendnum].is_online
|
||||
&& is_timeout(onion_c->friends_list[friendnum].last_seen, DEAD_ONION_TIMEOUT)) {
|
||||
onion_c->friends_list[friendnum].is_fake_clientid = 0;
|
||||
DHT_delfriend(onion_c->dht, onion_c->friends_list[friendnum].fake_client_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Function to call when onion data packet with contents beginning with byte is received. */
|
||||
void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_handler_callback cb, void *object)
|
||||
|
@ -1278,7 +1276,6 @@ void do_onion_client(Onion_Client *onion_c)
|
|||
if (onion_isconnected(onion_c)) {
|
||||
for (i = 0; i < onion_c->num_friends; ++i) {
|
||||
do_friend(onion_c, i);
|
||||
cleanup_friend(onion_c, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#define ONION_FAKEID_INTERVAL 30
|
||||
#define DHT_FAKEID_INTERVAL 20
|
||||
|
||||
#define NUMBER_ONION_PATHS 3
|
||||
#define NUMBER_ONION_PATHS 6
|
||||
|
||||
/* The timeout the first time the path is added and
|
||||
then for all the next consecutive times */
|
||||
|
@ -79,7 +79,6 @@ typedef struct {
|
|||
uint8_t is_online; /* Set by the onion_set_friend_status function. */
|
||||
|
||||
uint8_t is_fake_clientid; /* 0 if we don't know the fake client id of the other 1 if we do. */
|
||||
uint64_t fake_client_id_timestamp;
|
||||
uint8_t fake_client_id[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t real_client_id[crypto_box_PUBLICKEYBYTES];
|
||||
|
||||
|
@ -103,6 +102,10 @@ typedef struct {
|
|||
void *tcp_relay_node_callback_object;
|
||||
uint32_t tcp_relay_node_callback_number;
|
||||
|
||||
void (*dht_pk_callback)(void *data, int32_t number, const uint8_t *dht_public_key);
|
||||
void *dht_pk_callback_object;
|
||||
uint32_t dht_pk_callback_number;
|
||||
|
||||
uint32_t run_count;
|
||||
} Onion_Friend;
|
||||
|
||||
|
@ -205,6 +208,18 @@ int onion_getfriendip(const Onion_Client *onion_c, int friend_num, IP_Port *ip_p
|
|||
int recv_tcp_relay_handler(Onion_Client *onion_c, int friend_num, int (*tcp_relay_node_callback)(void *object,
|
||||
uint32_t number, IP_Port ip_port, const uint8_t *public_key), void *object, uint32_t number);
|
||||
|
||||
|
||||
/* Set the function for this friend that will be callbacked with object and number
|
||||
* when that friend gives us his DHT temporary public key.
|
||||
*
|
||||
* object and number will be passed as argument to this function.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int onion_dht_pk_callback(Onion_Client *onion_c, int friend_num, void (*function)(void *data, int32_t number,
|
||||
const uint8_t *dht_public_key), void *object, uint32_t number);
|
||||
|
||||
/* Set a friends DHT public key.
|
||||
* timestamp is the time (current_time_monotonic()) at which the key was last confirmed belonging to
|
||||
* the other peer.
|
||||
|
@ -212,14 +227,14 @@ int recv_tcp_relay_handler(Onion_Client *onion_c, int friend_num, int (*tcp_rela
|
|||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uint8_t *dht_key, uint64_t timestamp);
|
||||
int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uint8_t *dht_key);
|
||||
|
||||
/* Copy friends DHT public key into dht_key.
|
||||
*
|
||||
* return 0 on failure (no key copied).
|
||||
* return timestamp on success (key copied).
|
||||
* return 1 on success (key copied).
|
||||
*/
|
||||
uint64_t onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_num, uint8_t *dht_key);
|
||||
unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_num, uint8_t *dht_key);
|
||||
|
||||
#define ONION_DATA_IN_RESPONSE_MIN_SIZE (crypto_box_PUBLICKEYBYTES + crypto_box_MACBYTES)
|
||||
#define ONION_CLIENT_MAX_DATA_SIZE (MAX_DATA_REQUEST_SIZE - ONION_DATA_IN_RESPONSE_MIN_SIZE)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#endif
|
||||
|
||||
#include "Messenger.h"
|
||||
#include "group.h"
|
||||
#include "logger.h"
|
||||
|
||||
#define __TOX_DEFINED__
|
||||
|
@ -544,13 +545,15 @@ int tox_send_lossless_packet(const Tox *tox, int32_t friendnumber, const uint8_t
|
|||
|
||||
/* Set the callback for group invites.
|
||||
*
|
||||
* Function(Tox *tox, int32_t friendnumber, uint8_t *group_public_key, void *userdata)
|
||||
* Function(Tox *tox, int32_t friendnumber, uint8_t *data, uint16_t length, void *userdata)
|
||||
*
|
||||
* data of length is what needs to be passed to join_groupchat().
|
||||
*/
|
||||
void tox_callback_group_invite(Tox *tox, void (*function)(Messenger *tox, int32_t, const uint8_t *, void *),
|
||||
void tox_callback_group_invite(Tox *tox, void (*function)(Messenger *tox, int32_t, const uint8_t *, uint16_t, void *),
|
||||
void *userdata)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
m_callback_group_invite(m, function, userdata);
|
||||
g_callback_group_invite(m->group_chat_object, function, userdata);
|
||||
}
|
||||
|
||||
/* Set the callback for group messages.
|
||||
|
@ -561,7 +564,7 @@ void tox_callback_group_message(Tox *tox, void (*function)(Messenger *tox, int,
|
|||
void *userdata)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
m_callback_group_message(m, function, userdata);
|
||||
g_callback_group_message(m->group_chat_object, function, userdata);
|
||||
}
|
||||
|
||||
/* Set the callback for group actions.
|
||||
|
@ -572,7 +575,7 @@ void tox_callback_group_action(Tox *tox, void (*function)(Messenger *tox, int, i
|
|||
void *userdata)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
m_callback_group_action(m, function, userdata);
|
||||
//m_callback_group_action(m, function, userdata);
|
||||
}
|
||||
|
||||
/* Set callback function for peer name list changes.
|
||||
|
@ -583,7 +586,7 @@ void tox_callback_group_action(Tox *tox, void (*function)(Messenger *tox, int, i
|
|||
void tox_callback_group_namelist_change(Tox *tox, void (*function)(Tox *tox, int, int, uint8_t, void *), void *userdata)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
m_callback_group_namelistchange(m, function, userdata);
|
||||
//m_callback_group_namelistchange(m, function, userdata);
|
||||
}
|
||||
|
||||
/* Creates a new groupchat and puts it in the chats array.
|
||||
|
@ -594,8 +597,9 @@ void tox_callback_group_namelist_change(Tox *tox, void (*function)(Tox *tox, int
|
|||
int tox_add_groupchat(Tox *tox)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return add_groupchat(m);
|
||||
return add_groupchat(m->group_chat_object);
|
||||
}
|
||||
|
||||
/* Delete a groupchat from the chats array.
|
||||
*
|
||||
* return 0 on success.
|
||||
|
@ -604,7 +608,7 @@ int tox_add_groupchat(Tox *tox)
|
|||
int tox_del_groupchat(Tox *tox, int groupnumber)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return del_groupchat(m, groupnumber);
|
||||
return del_groupchat(m->group_chat_object, groupnumber);
|
||||
}
|
||||
|
||||
/* Copy the name of peernumber who is in groupnumber to name.
|
||||
|
@ -616,8 +620,10 @@ int tox_del_groupchat(Tox *tox, int groupnumber)
|
|||
int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t *name)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return m_group_peername(m, groupnumber, peernumber, name);
|
||||
//return m_group_peername(m, groupnumber, peernumber, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* invite friendnumber to groupnumber
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
|
@ -625,37 +631,40 @@ int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t
|
|||
int tox_invite_friend(Tox *tox, int32_t friendnumber, int groupnumber)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return invite_friend(m, friendnumber, groupnumber);
|
||||
return invite_friend(m->group_chat_object, friendnumber, groupnumber);
|
||||
}
|
||||
/* Join a group (you need to have been invited first.)
|
||||
|
||||
/* Join a group (you need to have been invited first.) using data of length obtained
|
||||
* in the group invite callback.
|
||||
*
|
||||
* returns group number on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *friend_group_public_key)
|
||||
int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return join_groupchat(m, friendnumber, friend_group_public_key);
|
||||
return join_groupchat(m->group_chat_object, friendnumber, data, length);
|
||||
}
|
||||
|
||||
/* send a group message
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint32_t length)
|
||||
int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint16_t length)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return group_message_send(m, groupnumber, message, length);
|
||||
return group_message_send(m->group_chat_object, groupnumber, message, length);
|
||||
}
|
||||
|
||||
/* send a group action
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint32_t length)
|
||||
int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint16_t length)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return group_action_send(m, groupnumber, action, length);
|
||||
//return group_action_send(m, groupnumber, action, length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the number of peers in the group chat on success.
|
||||
|
@ -664,7 +673,8 @@ int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint
|
|||
int tox_group_number_peers(const Tox *tox, int groupnumber)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return group_number_peers(m, groupnumber);
|
||||
//return group_number_peers(m, groupnumber);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* List all the peers in the group chat.
|
||||
|
@ -681,7 +691,8 @@ int tox_group_get_names(const Tox *tox, int groupnumber, uint8_t names[][TOX_MAX
|
|||
uint16_t length)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return group_names(m, groupnumber, names, lengths, length);
|
||||
//return group_names(m, groupnumber, names, lengths, length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the number of chats in the instance m.
|
||||
|
@ -690,7 +701,8 @@ int tox_group_get_names(const Tox *tox, int groupnumber, uint8_t names[][TOX_MAX
|
|||
uint32_t tox_count_chatlist(const Tox *tox)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return count_chatlist(m);
|
||||
//return count_chatlist(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy a list of valid chat IDs into the array out_list.
|
||||
|
@ -701,7 +713,8 @@ uint32_t tox_count_chatlist(const Tox *tox)
|
|||
uint32_t tox_get_chatlist(const Tox *tox, int *out_list, uint32_t list_size)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return copy_chatlist(m, out_list, list_size);
|
||||
//return copy_chatlist(m, out_list, list_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -820,6 +833,12 @@ int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t lengt
|
|||
return m_set_avatar(m, format, data, length);
|
||||
}
|
||||
|
||||
int tox_unset_avatar(Tox *tox)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return m_unset_avatar(m);
|
||||
}
|
||||
|
||||
int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
|
@ -947,7 +966,14 @@ Tox *tox_new(Tox_Options *options)
|
|||
}
|
||||
}
|
||||
|
||||
return new_messenger(&m_options);
|
||||
Messenger *m = new_messenger(&m_options);
|
||||
|
||||
if (!new_groupchats(m)) {
|
||||
kill_messenger(m);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/* Run this before closing shop.
|
||||
|
@ -956,6 +982,7 @@ Tox *tox_new(Tox_Options *options)
|
|||
void tox_kill(Tox *tox)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
kill_groupchats(m->group_chat_object);
|
||||
kill_messenger(m);
|
||||
}
|
||||
|
||||
|
|
|
@ -419,9 +419,12 @@ int tox_send_lossless_packet(const Tox *tox, int32_t friendnumber, const uint8_t
|
|||
|
||||
/* Set the callback for group invites.
|
||||
*
|
||||
* Function(Tox *tox, int friendnumber, uint8_t *group_public_key, void *userdata)
|
||||
* Function(Tox *tox, int32_t friendnumber, uint8_t *data, uint16_t length, void *userdata)
|
||||
*
|
||||
* data of length is what needs to be passed to join_groupchat().
|
||||
*/
|
||||
void tox_callback_group_invite(Tox *tox, void (*function)(Tox *tox, int32_t, const uint8_t *, void *), void *userdata);
|
||||
void tox_callback_group_invite(Tox *tox, void (*function)(Tox *tox, int32_t, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Set the callback for group messages.
|
||||
*
|
||||
|
@ -479,24 +482,25 @@ int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t
|
|||
*/
|
||||
int tox_invite_friend(Tox *tox, int32_t friendnumber, int groupnumber);
|
||||
|
||||
/* Join a group (you need to have been invited first.)
|
||||
/* Join a group (you need to have been invited first.) using data of length obtained
|
||||
* in the group invite callback.
|
||||
*
|
||||
* returns group number on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *friend_group_public_key);
|
||||
int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length);
|
||||
|
||||
/* send a group message
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint32_t length);
|
||||
int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint16_t length);
|
||||
|
||||
/* send a group action
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint32_t length);
|
||||
int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint16_t length);
|
||||
|
||||
/* Return the number of peers in the group chat on success.
|
||||
* return -1 on failure
|
||||
|
@ -539,8 +543,8 @@ uint32_t tox_get_chatlist(const Tox *tox, int *out_list, uint32_t list_size);
|
|||
* function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata)
|
||||
*
|
||||
* where 'format' is the avatar image format (see TOX_AVATAR_FORMAT) and 'hash' is the hash of
|
||||
* the avatar data for caching purposes and it is exactly TOX_AVATAR_HASH_LENGTH long. If the
|
||||
* image format is NONE, the hash is zeroed.
|
||||
* the avatar data for caching purposes and it is exactly TOX_HASH_LENGTH long. If the image
|
||||
* format is NONE, the hash is zeroed.
|
||||
*
|
||||
*/
|
||||
void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t *, void *),
|
||||
|
@ -556,12 +560,12 @@ void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint
|
|||
*
|
||||
* where 'format' is the avatar image format (see TOX_AVATAR_FORMAT); 'hash' is the
|
||||
* locally-calculated cryptographic hash of the avatar data and it is exactly
|
||||
* TOX_AVATAR_HASH_LENGTH long; 'data' is the avatar image data and 'datalen' is the length
|
||||
* TOX_HASH_LENGTH long; 'data' is the avatar image data and 'datalen' is the length
|
||||
* of such data.
|
||||
*
|
||||
* If format is NONE, 'data' is NULL, 'datalen' is zero, and the hash is zeroed. The hash is
|
||||
* always validated locally with the function tox_avatar_hash and ensured to match the image
|
||||
* data, so this value can be safely used to compare with cached avatars.
|
||||
* always validated locally with the function tox_hash and ensured to match the image data,
|
||||
* so this value can be safely used to compare with cached avatars.
|
||||
*
|
||||
* WARNING: users MUST treat all avatar image data received from another peer as untrusted and
|
||||
* potentially malicious. The library only ensures that the data which arrived is the same the
|
||||
|
@ -586,6 +590,10 @@ void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint
|
|||
*/
|
||||
int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length);
|
||||
|
||||
/* Unsets the user avatar.
|
||||
|
||||
returns 0 on success (currently always returns 0) */
|
||||
int tox_unset_avatar(Tox *tox);
|
||||
|
||||
/* Get avatar data from the current user.
|
||||
* Copies the current user avatar data to the destination buffer and sets the image format
|
||||
|
@ -601,7 +609,7 @@ int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t lengt
|
|||
* buf - destination buffer to the image data. Must have at least 'maxlen' bytes;
|
||||
* length - destination pointer to the image data length;
|
||||
* maxlen - length of the destination buffer 'buf';
|
||||
* hash - destination pointer to the avatar hash (it must be exactly TOX_AVATAR_HASH_LENGTH bytes long).
|
||||
* hash - destination pointer to the avatar hash (it must be exactly TOX_HASH_LENGTH bytes long).
|
||||
*
|
||||
* returns 0 on success;
|
||||
* returns -1 on failure.
|
||||
|
|
Loading…
Reference in New Issue
Block a user