summaryrefslogtreecommitdiff
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
parentcaca350f435cf6a2a3778c64771dafdb7dde8b7a (diff)
Implement conferences saving
* add global friend_connection status callback, used for group rejoining * stop leaving groups on killing tox
-rw-r--r--auto_tests/conference_test.c34
-rw-r--r--docs/minpgc.md14
-rw-r--r--toxcore/Messenger.h3
-rw-r--r--toxcore/friend_connection.c16
-rw-r--r--toxcore/friend_connection.h5
-rw-r--r--toxcore/group.c259
-rw-r--r--toxcore/group.h11
-rw-r--r--toxcore/state.h1
-rw-r--r--toxcore/tox.c1
9 files changed, 327 insertions, 17 deletions
diff --git a/auto_tests/conference_test.c b/auto_tests/conference_test.c
index 647b649d..349c2905 100644
--- a/auto_tests/conference_test.c
+++ b/auto_tests/conference_test.c
@@ -167,13 +167,34 @@ static void run_conference_tests(Tox **toxes, State *state)
167 167
168 printf("letting random toxes timeout\n"); 168 printf("letting random toxes timeout\n");
169 bool disconnected[NUM_GROUP_TOX] = {0}; 169 bool disconnected[NUM_GROUP_TOX] = {0};
170 bool restarting[NUM_GROUP_TOX] = {0};
170 171
171 ck_assert(NUM_DISCONNECT < NUM_GROUP_TOX); 172 ck_assert(NUM_DISCONNECT < NUM_GROUP_TOX);
172 173
173 for (uint16_t i = 0; i < NUM_DISCONNECT; ++i) { 174 for (uint16_t i = 0; i < NUM_DISCONNECT; ++i) {
174 uint32_t disconnect = random_false_index(disconnected, NUM_GROUP_TOX); 175 uint32_t disconnect = random_false_index(disconnected, NUM_GROUP_TOX);
175 disconnected[disconnect] = true; 176 disconnected[disconnect] = true;
176 printf("Disconnecting #%u\n", state[disconnect].index); 177
178 if (i < NUM_DISCONNECT / 2) {
179 restarting[disconnect] = true;
180 printf("Restarting #%u\n", state[disconnect].index);
181 } else {
182 printf("Disconnecting #%u\n", state[disconnect].index);
183 }
184 }
185
186 uint8_t *save[NUM_GROUP_TOX];
187 size_t save_size[NUM_GROUP_TOX];
188
189 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
190 if (restarting[i]) {
191 save_size[i] = tox_get_savedata_size(toxes[i]);
192 ck_assert_msg(save_size[i] != 0, "save is invalid size %u", (unsigned)save_size[i]);
193 save[i] = (uint8_t *)malloc(save_size[i]);
194 ck_assert_msg(save[i] != nullptr, "malloc failed");
195 tox_get_savedata(toxes[i], save[i]);
196 tox_kill(toxes[i]);
197 }
177 } 198 }
178 199
179 do { 200 do {
@@ -187,6 +208,17 @@ static void run_conference_tests(Tox **toxes, State *state)
187 c_sleep(20); 208 c_sleep(20);
188 } while (!toxes_are_disconnected_from_group(NUM_GROUP_TOX, toxes, NUM_DISCONNECT, disconnected)); 209 } while (!toxes_are_disconnected_from_group(NUM_GROUP_TOX, toxes, NUM_DISCONNECT, disconnected));
189 210
211 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
212 if (restarting[i]) {
213 struct Tox_Options *const options = tox_options_new(nullptr);
214 tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
215 tox_options_set_savedata_data(options, save[i], save_size[i]);
216 toxes[i] = tox_new_log(options, nullptr, &state[i].index);
217 tox_options_free(options);
218 free(save[i]);
219 }
220 }
221
190 if (check_name_change_propagation) { 222 if (check_name_change_propagation) {
191 printf("changing names\n"); 223 printf("changing names\n");
192 224
diff --git a/docs/minpgc.md b/docs/minpgc.md
index aa2ed1dc..a09895bd 100644
--- a/docs/minpgc.md
+++ b/docs/minpgc.md
@@ -29,7 +29,7 @@ packet from it, or a New Peer message for it.
29 29
30If a frozen peer is seen to be active, we remove its 'frozen' flag and send a 30If a frozen peer is seen to be active, we remove its 'frozen' flag and send a
31Name group message. (We can hold off on sending this message until the next 31Name group message. (We can hold off on sending this message until the next
32tox\_iterate, and only send one message if many frozen peers become active at 32`tox_iterate`, and only send one message if many frozen peers become active at
33once). 33once).
34 34
35If we receive a New Peer message for a peer, we update its DHT pubkey. 35If we receive a New Peer message for a peer, we update its DHT pubkey.
@@ -102,13 +102,13 @@ actually make more sense in the implementation to have a separate list for
102frozen peers. 102frozen peers.
103 103
104## Saving 104## Saving
105Saving could be implemented by simply saving all live groups with their group 105Saving is implemented by simply saving all live groups with their group numbers
106numbers and full peer info for all peers. On reload, all peers would be set as 106and full peer info for all peers. On reload, all peers are set as frozen.
107frozen.
108 107
109The client would need to support this by understanding that these groups exist 108Clients needs to support this by understanding that groups may exist on
110on start-up (e.g. starting windows for them), and by not automatically killing 109start-up. Clients should call `tox_conference_get_chatlist` to obtain them. A
111groups on closing the client. 110group which is deleted (with `tox_conference_delete`) is removed permanently
111and will not be saved.
112 112
113## Limitations 113## Limitations
114If a peer disconnects from the group for a period short enough that group 114If a peer disconnects from the group for a period short enough that group
diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h
index 1651315e..a3376e23 100644
--- a/toxcore/Messenger.h
+++ b/toxcore/Messenger.h
@@ -796,6 +796,9 @@ uint8_t *messenger_save(const Messenger *m, uint8_t *data);
796 796
797/* Load a state section. 797/* Load a state section.
798 * 798 *
799 * @param data Data to load.
800 * @param length Length of data.
801 * @param type Type of section (STATE_TYPE_*).
799 * @param status Result of loading section is stored here if the section is handled. 802 * @param status Result of loading section is stored here if the section is handled.
800 * @return true iff section handled. 803 * @return true iff section handled.
801 */ 804 */
diff --git a/toxcore/friend_connection.c b/toxcore/friend_connection.c
index 9c805b45..69533def 100644
--- a/toxcore/friend_connection.c
+++ b/toxcore/friend_connection.c
@@ -85,6 +85,9 @@ struct Friend_Connections {
85 fr_request_cb *fr_request_callback; 85 fr_request_cb *fr_request_callback;
86 void *fr_request_object; 86 void *fr_request_object;
87 87
88 global_status_cb *global_status_callback;
89 void *global_status_callback_object;
90
88 uint64_t last_lan_discovery; 91 uint64_t last_lan_discovery;
89 uint16_t next_lan_port; 92 uint16_t next_lan_port;
90 93
@@ -401,9 +404,11 @@ static int handle_status(void *object, int number, uint8_t status, void *userdat
401 } 404 }
402 405
403 if (status_changed) { 406 if (status_changed) {
404 unsigned int i; 407 if (fr_c->global_status_callback) {
408 fr_c->global_status_callback(fr_c->global_status_callback_object, number, status, userdata);
409 }
405 410
406 for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) { 411 for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
407 if (friend_con->callbacks[i].status_callback) { 412 if (friend_con->callbacks[i].status_callback) {
408 friend_con->callbacks[i].status_callback( 413 friend_con->callbacks[i].status_callback(
409 friend_con->callbacks[i].callback_object, 414 friend_con->callbacks[i].callback_object,
@@ -716,6 +721,13 @@ int friend_connection_callbacks(Friend_Connections *fr_c, int friendcon_id, unsi
716 return 0; 721 return 0;
717} 722}
718 723
724/* Set global status callback for friend connections. */
725void set_global_status_callback(Friend_Connections *fr_c, global_status_cb *global_status_callback, void *object)
726{
727 fr_c->global_status_callback = global_status_callback;
728 fr_c->global_status_callback_object = object;
729}
730
719/* return the crypt_connection_id for the connection. 731/* return the crypt_connection_id for the connection.
720 * 732 *
721 * return crypt_connection_id on success. 733 * return crypt_connection_id on success.
diff --git a/toxcore/friend_connection.h b/toxcore/friend_connection.h
index 149a4fa7..166c731b 100644
--- a/toxcore/friend_connection.h
+++ b/toxcore/friend_connection.h
@@ -101,10 +101,15 @@ void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *
101 */ 101 */
102int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, IP_Port ip_port, const uint8_t *public_key); 102int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, IP_Port ip_port, const uint8_t *public_key);
103 103
104typedef int global_status_cb(void *object, int id, uint8_t status, void *userdata);
105
104typedef int fc_status_cb(void *object, int id, uint8_t status, void *userdata); 106typedef int fc_status_cb(void *object, int id, uint8_t status, void *userdata);
105typedef int fc_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata); 107typedef int fc_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata);
106typedef int fc_lossy_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata); 108typedef int fc_lossy_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata);
107 109
110/* Set global status callback for friend connections. */
111void set_global_status_callback(Friend_Connections *fr_c, global_status_cb *global_status_callback, void *object);
112
108/* Set the callbacks for the friend connection. 113/* Set the callbacks for the friend connection.
109 * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array. 114 * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array.
110 * 115 *
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
diff --git a/toxcore/group.h b/toxcore/group.h
index 8673abdb..148de0c1 100644
--- a/toxcore/group.h
+++ b/toxcore/group.h
@@ -306,6 +306,12 @@ int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8
306 */ 306 */
307int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length); 307int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length);
308 308
309/* send message to announce leaving group
310 * return true on success
311 * return false on failure
312 */
313bool group_leave(const Group_Chats *g_c, uint32_t groupnumber);
314
309/* set the group's title, limited to MAX_NAME_LENGTH 315/* set the group's title, limited to MAX_NAME_LENGTH
310 * return 0 on success 316 * return 0 on success
311 * return -1 if groupnumber is invalid. 317 * return -1 if groupnumber is invalid.
@@ -455,8 +461,11 @@ uint32_t conferences_size(const Group_Chats *g_c);
455uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data); 461uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data);
456 462
457/** 463/**
458 * Load a section. 464 * Load a state section.
459 * 465 *
466 * @param data Data to load
467 * @param length Length of data
468 * @param type Type of section (STATE_TYPE_*)
460 * @param status Result of loading section is stored here if the section is handled. 469 * @param status Result of loading section is stored here if the section is handled.
461 * @return true iff section handled. 470 * @return true iff section handled.
462 */ 471 */
diff --git a/toxcore/state.h b/toxcore/state.h
index 872b1e9d..6e3c897e 100644
--- a/toxcore/state.h
+++ b/toxcore/state.h
@@ -31,6 +31,7 @@ typedef enum State_Type {
31 STATE_TYPE_STATUS = 6, 31 STATE_TYPE_STATUS = 6,
32 STATE_TYPE_TCP_RELAY = 10, 32 STATE_TYPE_TCP_RELAY = 10,
33 STATE_TYPE_PATH_NODE = 11, 33 STATE_TYPE_PATH_NODE = 11,
34 STATE_TYPE_CONFERENCES = 20,
34 STATE_TYPE_END = 255, 35 STATE_TYPE_END = 255,
35} State_Type; 36} State_Type;
36 37
diff --git a/toxcore/tox.c b/toxcore/tox.c
index 629cc1c5..88a9bd50 100644
--- a/toxcore/tox.c
+++ b/toxcore/tox.c
@@ -1533,6 +1533,7 @@ uint32_t tox_conference_new(Tox *tox, Tox_Err_Conference_New *error)
1533bool tox_conference_delete(Tox *tox, uint32_t conference_number, Tox_Err_Conference_Delete *error) 1533bool tox_conference_delete(Tox *tox, uint32_t conference_number, Tox_Err_Conference_Delete *error)
1534{ 1534{
1535 Messenger *m = tox->m; 1535 Messenger *m = tox->m;
1536 group_leave(m->conferences_object, conference_number);
1536 int ret = del_groupchat(m->conferences_object, conference_number); 1537 int ret = del_groupchat(m->conferences_object, conference_number);
1537 1538
1538 if (ret == -1) { 1539 if (ret == -1) {