summaryrefslogtreecommitdiff
path: root/toxcore/group.c
diff options
context:
space:
mode:
authorzugz (tox) <mbays+tox@sdf.org>2018-09-09 23:27:39 +0200
committerzugz (tox) <mbays+tox@sdf.org>2018-11-29 21:52:23 +0100
commit9770880e975a09635a461c46c8fcc193bce57004 (patch)
tree8c7aa3c3ee8d264a3fdd97d8a5c49caa25f5da0b /toxcore/group.c
parentcaca350f435cf6a2a3778c64771dafdb7dde8b7a (diff)
Implement conferences saving
* add global friend_connection status callback, used for group rejoining * stop leaving groups on killing tox
Diffstat (limited to 'toxcore/group.c')
-rw-r--r--toxcore/group.c259
1 files changed, 253 insertions, 6 deletions
diff --git a/toxcore/group.c b/toxcore/group.c
index 9ef32b72..99f72336 100644
--- a/toxcore/group.c
+++ b/toxcore/group.c
@@ -27,10 +27,12 @@
27 27
28#include "group.h" 28#include "group.h"
29 29
30#include <assert.h>
30#include <stdlib.h> 31#include <stdlib.h>
31#include <string.h> 32#include <string.h>
32 33
33#include "mono_time.h" 34#include "mono_time.h"
35#include "state.h"
34#include "util.h" 36#include "util.h"
35 37
36/** 38/**
@@ -880,13 +882,23 @@ static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id)
880 } 882 }
881} 883}
882 884
885static int g_handle_any_status(void *object, int friendcon_id, uint8_t status, void *userdata)
886{
887 Group_Chats *g_c = (Group_Chats *)object;
888
889 if (status) {
890 rejoin_frozen_friend(g_c, friendcon_id);
891 }
892
893 return 0;
894}
895
883static int g_handle_status(void *object, int friendcon_id, uint8_t status, void *userdata) 896static int g_handle_status(void *object, int friendcon_id, uint8_t status, void *userdata)
884{ 897{
885 Group_Chats *g_c = (Group_Chats *)object; 898 Group_Chats *g_c = (Group_Chats *)object;
886 899
887 if (status) { /* Went online */ 900 if (status) { /* Went online */
888 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_ONLINE, userdata); 901 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_ONLINE, userdata);
889 rejoin_frozen_friend(g_c, friendcon_id);
890 } else { /* Went offline */ 902 } else { /* Went offline */
891 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_CONNECTION, userdata); 903 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_CONNECTION, userdata);
892 // TODO(irungentoo): remove timedout connections? 904 // TODO(irungentoo): remove timedout connections?
@@ -1021,7 +1033,6 @@ int add_groupchat(Group_Chats *g_c, uint8_t type)
1021 return groupnumber; 1033 return groupnumber;
1022} 1034}
1023 1035
1024static int group_kill_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num);
1025/* Delete a groupchat from the chats array. 1036/* Delete a groupchat from the chats array.
1026 * 1037 *
1027 * return 0 on success. 1038 * return 0 on success.
@@ -1035,8 +1046,6 @@ int del_groupchat(Group_Chats *g_c, uint32_t groupnumber)
1035 return -1; 1046 return -1;
1036 } 1047 }
1037 1048
1038 group_kill_peer_send(g_c, groupnumber, g->peer_number);
1039
1040 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { 1049 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1041 if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { 1050 if (g->close[i].type == GROUPCHAT_CLOSE_NONE) {
1042 continue; 1051 continue;
@@ -1587,6 +1596,22 @@ static int group_name_send(const Group_Chats *g_c, uint32_t groupnumber, const u
1587 return -1; 1596 return -1;
1588} 1597}
1589 1598
1599/* send message to announce leaving group
1600 * return true on success
1601 * return false on failure
1602 */
1603bool group_leave(const Group_Chats *g_c, uint32_t groupnumber)
1604{
1605 Group_c *g = get_group_c(g_c, groupnumber);
1606
1607 if (!g) {
1608 return false;
1609 }
1610
1611 return group_kill_peer_send(g_c, groupnumber, g->peer_number) == 0;
1612}
1613
1614
1590/* set the group's title, limited to MAX_NAME_LENGTH 1615/* set the group's title, limited to MAX_NAME_LENGTH
1591 * return 0 on success 1616 * return 0 on success
1592 * return -1 if groupnumber is invalid. 1617 * return -1 if groupnumber is invalid.
@@ -2875,22 +2900,242 @@ void send_name_all_groups(Group_Chats *g_c)
2875 } 2900 }
2876} 2901}
2877 2902
2903#define SAVED_PEER_SIZE_CONSTANT (2 * CRYPTO_PUBLIC_KEY_SIZE + 2 + 1)
2904
2905static uint32_t saved_peer_size(const Group_Peer *peer)
2906{
2907 return SAVED_PEER_SIZE_CONSTANT + peer->nick_len;
2908}
2909
2910static uint8_t *save_peer(const Group_Peer *peer, uint8_t *data)
2911{
2912 memcpy(data, peer->real_pk, CRYPTO_PUBLIC_KEY_SIZE);
2913 data += CRYPTO_PUBLIC_KEY_SIZE;
2914
2915 memcpy(data, peer->temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
2916 data += CRYPTO_PUBLIC_KEY_SIZE;
2917
2918 host_to_lendian_bytes16(data, peer->peer_number);
2919 data += sizeof(uint16_t);
2920
2921 *data = peer->nick_len;
2922 ++data;
2923
2924 memcpy(data, peer->nick, peer->nick_len);
2925 data += peer->nick_len;
2926
2927 return data;
2928}
2929
2930#define SAVED_CONF_SIZE_CONSTANT (1 + GROUP_ID_LENGTH + sizeof(uint32_t) \
2931 + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + 1)
2932
2933static uint32_t saved_conf_size(const Group_c *g)
2934{
2935 uint32_t len = SAVED_CONF_SIZE_CONSTANT + g->title_len;
2936
2937 for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) {
2938 const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers];
2939
2940 if (id_equal(peer->real_pk, g->real_pk)) {
2941 continue;
2942 }
2943
2944 len += saved_peer_size(peer);
2945 }
2946
2947 return len;
2948}
2949
2950static uint8_t *save_conf(const Group_c *g, uint8_t *data)
2951{
2952 *data = g->type;
2953 ++data;
2954
2955 memcpy(data, g->id, GROUP_ID_LENGTH);
2956 data += GROUP_ID_LENGTH;
2957
2958 host_to_lendian_bytes32(data, g->message_number);
2959 data += sizeof(uint32_t);
2960
2961 host_to_lendian_bytes16(data, g->lossy_message_number);
2962 data += sizeof(uint16_t);
2963
2964 host_to_lendian_bytes16(data, g->peer_number);
2965 data += sizeof(uint16_t);
2966
2967 host_to_lendian_bytes32(data, g->numpeers - 1 + g->numfrozen);
2968 data += sizeof(uint32_t);
2969
2970 *data = g->title_len;
2971 ++data;
2972
2973 memcpy(data, g->title, g->title_len);
2974 data += g->title_len;
2975
2976#ifndef NDEBUG
2977 bool found_self = false;
2978#endif
2979
2980 for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) {
2981 const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers];
2982
2983 if (id_equal(peer->real_pk, g->real_pk)) {
2984#ifndef NDEBUG
2985 found_self = true;
2986#endif
2987 continue;
2988 }
2989
2990 data = save_peer(peer, data);
2991 }
2992
2993 assert(found_self);
2994
2995 return data;
2996}
2997
2998static uint32_t conferences_section_size(const Group_Chats *g_c)
2999{
3000 uint32_t len = 0;
3001
3002 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3003 Group_c *g = get_group_c(g_c, i);
3004
3005 if (!g || g->status != GROUPCHAT_STATUS_CONNECTED) {
3006 continue;
3007 }
3008
3009 len += saved_conf_size(g);
3010 }
3011
3012 return len;
3013}
3014
2878uint32_t conferences_size(const Group_Chats *g_c) 3015uint32_t conferences_size(const Group_Chats *g_c)
2879{ 3016{
2880 return 0; 3017 return 2 * sizeof(uint32_t) + conferences_section_size(g_c);
2881} 3018}
2882 3019
2883uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data) 3020uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data)
2884{ 3021{
3022 const uint32_t len = conferences_section_size(g_c);
3023 data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_CONFERENCES);
3024
3025 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3026 Group_c *g = get_group_c(g_c, i);
3027
3028 if (!g || g->status != GROUPCHAT_STATUS_CONNECTED) {
3029 continue;
3030 }
3031
3032 data = save_conf(g, data);
3033 }
3034
2885 return data; 3035 return data;
2886} 3036}
2887 3037
3038static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data, uint32_t length)
3039{
3040 const uint8_t *init_data = data;
3041
3042 while (length >= (uint32_t)(data - init_data) + SAVED_CONF_SIZE_CONSTANT) {
3043 const int groupnumber = create_group_chat(g_c);
3044
3045 if (groupnumber == -1) {
3046 return STATE_LOAD_STATUS_ERROR;
3047 }
3048
3049 Group_c *g = &g_c->chats[groupnumber];
3050
3051 g->type = *data;
3052 ++data;
3053
3054 memcpy(g->id, data, GROUP_ID_LENGTH);
3055 data += GROUP_ID_LENGTH;
3056
3057 lendian_bytes_to_host32(&g->message_number, data);
3058 data += sizeof(uint32_t);
3059
3060 lendian_bytes_to_host16(&g->lossy_message_number, data);
3061 data += sizeof(uint16_t);
3062
3063 lendian_bytes_to_host16(&g->peer_number, data);
3064 data += sizeof(uint16_t);
3065
3066 lendian_bytes_to_host32(&g->numfrozen, data);
3067 data += sizeof(uint32_t);
3068
3069 g->frozen = (Group_Peer *)malloc(sizeof(Group_Peer) * g->numfrozen);
3070
3071 if (g->frozen == nullptr) {
3072 return STATE_LOAD_STATUS_ERROR;
3073 }
3074
3075 g->title_len = *data;
3076 ++data;
3077
3078 if (length < (uint32_t)(data - init_data) + g->title_len) {
3079 return STATE_LOAD_STATUS_ERROR;
3080 }
3081
3082 memcpy(g->title, data, g->title_len);
3083 data += g->title_len;
3084
3085 for (uint32_t j = 0; j < g->numfrozen; ++j) {
3086 if (length < (uint32_t)(data - init_data) + SAVED_PEER_SIZE_CONSTANT) {
3087 return STATE_LOAD_STATUS_ERROR;
3088 }
3089
3090 Group_Peer *peer = &g->frozen[j];
3091 memset(peer, 0, sizeof(Group_Peer));
3092
3093 id_copy(peer->real_pk, data);
3094 data += CRYPTO_PUBLIC_KEY_SIZE;
3095 id_copy(peer->temp_pk, data);
3096 data += CRYPTO_PUBLIC_KEY_SIZE;
3097
3098 lendian_bytes_to_host16(&peer->peer_number, data);
3099 data += sizeof(uint16_t);
3100
3101 peer->nick_len = *data;
3102 ++data;
3103
3104 if (length < (uint32_t)(data - init_data) + peer->nick_len) {
3105 return STATE_LOAD_STATUS_ERROR;
3106 }
3107
3108 memcpy(peer->nick, data, peer->nick_len);
3109 data += peer->nick_len;
3110 }
3111
3112 g->status = GROUPCHAT_STATUS_CONNECTED;
3113 memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
3114 const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), g->peer_number,
3115 nullptr, true, false);
3116
3117 if (peer_index == -1) {
3118 return STATE_LOAD_STATUS_ERROR;
3119 }
3120
3121 setnick(g_c, groupnumber, peer_index, g_c->m->name, g_c->m->name_length, nullptr, false);
3122 }
3123
3124 return STATE_LOAD_STATUS_CONTINUE;
3125}
3126
2888bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type, 3127bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type,
2889 State_Load_Status *status) 3128 State_Load_Status *status)
2890{ 3129{
2891 return false; 3130 if (type != STATE_TYPE_CONFERENCES) {
3131 return false;
3132 }
3133
3134 *status = load_conferences(g_c, data, length);
3135 return true;
2892} 3136}
2893 3137
3138
2894/* Create new groupchat instance. */ 3139/* Create new groupchat instance. */
2895Group_Chats *new_groupchats(Mono_Time *mono_time, Messenger *m) 3140Group_Chats *new_groupchats(Mono_Time *mono_time, Messenger *m)
2896{ 3141{
@@ -2910,6 +3155,8 @@ Group_Chats *new_groupchats(Mono_Time *mono_time, Messenger *m)
2910 m->conferences_object = temp; 3155 m->conferences_object = temp;
2911 m_callback_conference_invite(m, &handle_friend_invite_packet); 3156 m_callback_conference_invite(m, &handle_friend_invite_packet);
2912 3157
3158 set_global_status_callback(m->fr_c, &g_handle_any_status, temp);
3159
2913 return temp; 3160 return temp;
2914} 3161}
2915 3162