diff options
Diffstat (limited to 'toxcore/group.c')
-rw-r--r-- | toxcore/group.c | 259 |
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 | ||
885 | static 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 | |||
883 | static int g_handle_status(void *object, int friendcon_id, uint8_t status, void *userdata) | 896 | static 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 | ||
1024 | static 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 | */ | ||
1603 | bool 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 | |||
2905 | static uint32_t saved_peer_size(const Group_Peer *peer) | ||
2906 | { | ||
2907 | return SAVED_PEER_SIZE_CONSTANT + peer->nick_len; | ||
2908 | } | ||
2909 | |||
2910 | static 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 | |||
2933 | static 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 | |||
2950 | static 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 | |||
2998 | static 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 | |||
2878 | uint32_t conferences_size(const Group_Chats *g_c) | 3015 | uint32_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 | ||
2883 | uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data) | 3020 | uint8_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 | ||
3038 | static 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 | |||
2888 | bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type, | 3127 | bool 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. */ |
2895 | Group_Chats *new_groupchats(Mono_Time *mono_time, Messenger *m) | 3140 | Group_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 | ||