summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toxav/groupav.c4
-rw-r--r--toxcore/group.c116
-rw-r--r--toxcore/group.h44
-rw-r--r--toxcore/state.c22
-rw-r--r--toxcore/state.h3
-rw-r--r--toxcore/tox.api.h92
-rw-r--r--toxcore/tox.c112
-rw-r--r--toxcore/tox.h36
8 files changed, 347 insertions, 82 deletions
diff --git a/toxav/groupav.c b/toxav/groupav.c
index 55a5fe55..4c16d1de 100644
--- a/toxav/groupav.c
+++ b/toxav/groupav.c
@@ -473,7 +473,7 @@ int add_av_groupchat(const Logger *log, Tox *tox, Group_Chats *g_c, audio_data_c
473 } 473 }
474 474
475 if (groupchat_enable_av(log, tox, g_c, groupnumber, audio_callback, userdata) == -1) { 475 if (groupchat_enable_av(log, tox, g_c, groupnumber, audio_callback, userdata) == -1) {
476 del_groupchat(g_c, groupnumber); 476 del_groupchat(g_c, groupnumber, true);
477 return -1; 477 return -1;
478 } 478 }
479 479
@@ -495,7 +495,7 @@ int join_av_groupchat(const Logger *log, Tox *tox, Group_Chats *g_c, uint32_t fr
495 } 495 }
496 496
497 if (groupchat_enable_av(log, tox, g_c, groupnumber, audio_callback, userdata) == -1) { 497 if (groupchat_enable_av(log, tox, g_c, groupnumber, audio_callback, userdata) == -1) {
498 del_groupchat(g_c, groupnumber); 498 del_groupchat(g_c, groupnumber, true);
499 return -1; 499 return -1;
500 } 500 }
501 501
diff --git a/toxcore/group.c b/toxcore/group.c
index 99f72336..b345a01c 100644
--- a/toxcore/group.c
+++ b/toxcore/group.c
@@ -271,6 +271,7 @@ typedef enum Groupchat_Closest {
271 271
272static int add_to_closest(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk) 272static int add_to_closest(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk)
273{ 273{
274 // TODO(zugz): this can be const. Similarly throughout the file.
274 Group_c *g = get_group_c(g_c, groupnumber); 275 Group_c *g = get_group_c(g_c, groupnumber);
275 276
276 if (!g) { 277 if (!g) {
@@ -1033,12 +1034,15 @@ int add_groupchat(Group_Chats *g_c, uint8_t type)
1033 return groupnumber; 1034 return groupnumber;
1034} 1035}
1035 1036
1036/* Delete a groupchat from the chats array. 1037static int group_leave(const Group_Chats *g_c, uint32_t groupnumber);
1038
1039/* Delete a groupchat from the chats array, informing the group first as
1040 * appropriate.
1037 * 1041 *
1038 * return 0 on success. 1042 * return 0 on success.
1039 * return -1 if groupnumber is invalid. 1043 * return -1 if groupnumber is invalid.
1040 */ 1044 */
1041int del_groupchat(Group_Chats *g_c, uint32_t groupnumber) 1045int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently)
1042{ 1046{
1043 Group_c *g = get_group_c(g_c, groupnumber); 1047 Group_c *g = get_group_c(g_c, groupnumber);
1044 1048
@@ -1046,6 +1050,10 @@ int del_groupchat(Group_Chats *g_c, uint32_t groupnumber)
1046 return -1; 1050 return -1;
1047 } 1051 }
1048 1052
1053 if (leave_permanently) {
1054 group_leave(g_c, groupnumber);
1055 }
1056
1049 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { 1057 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1050 if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { 1058 if (g->close[i].type == GROUPCHAT_CLOSE_NONE) {
1051 continue; 1059 continue;
@@ -1071,14 +1079,14 @@ int del_groupchat(Group_Chats *g_c, uint32_t groupnumber)
1071 return wipe_group_chat(g_c, groupnumber); 1079 return wipe_group_chat(g_c, groupnumber);
1072} 1080}
1073 1081
1074/* Copy the public key of peernumber who is in groupnumber to pk. 1082/* Copy the public key of (frozen, if frozen is true) peernumber who is in
1075 * pk must be CRYPTO_PUBLIC_KEY_SIZE long. 1083 * groupnumber to pk. pk must be CRYPTO_PUBLIC_KEY_SIZE long.
1076 * 1084 *
1077 * return 0 on success 1085 * return 0 on success
1078 * return -1 if groupnumber is invalid. 1086 * return -1 if groupnumber is invalid.
1079 * return -2 if peernumber is invalid. 1087 * return -2 if peernumber is invalid.
1080 */ 1088 */
1081int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *pk) 1089int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *pk, bool frozen)
1082{ 1090{
1083 Group_c *g = get_group_c(g_c, groupnumber); 1091 Group_c *g = get_group_c(g_c, groupnumber);
1084 1092
@@ -1086,21 +1094,24 @@ int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, int peernumb
1086 return -1; 1094 return -1;
1087 } 1095 }
1088 1096
1089 if ((uint32_t)peernumber >= g->numpeers) { 1097 const Group_Peer *list = frozen ? g->frozen : g->group;
1098 const uint32_t num = frozen ? g->numfrozen : g->numpeers;
1099
1100 if ((uint32_t)peernumber >= num) {
1090 return -2; 1101 return -2;
1091 } 1102 }
1092 1103
1093 memcpy(pk, g->group[peernumber].real_pk, CRYPTO_PUBLIC_KEY_SIZE); 1104 memcpy(pk, list[peernumber].real_pk, CRYPTO_PUBLIC_KEY_SIZE);
1094 return 0; 1105 return 0;
1095} 1106}
1096 1107
1097/* 1108/*
1098 * Return the size of peernumber's name. 1109 * Return the size of (frozen, if frozen is true) peernumber's name.
1099 * 1110 *
1100 * return -1 if groupnumber is invalid. 1111 * return -1 if groupnumber is invalid.
1101 * return -2 if peernumber is invalid. 1112 * return -2 if peernumber is invalid.
1102 */ 1113 */
1103int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, int peernumber) 1114int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, bool frozen)
1104{ 1115{
1105 Group_c *g = get_group_c(g_c, groupnumber); 1116 Group_c *g = get_group_c(g_c, groupnumber);
1106 1117
@@ -1108,25 +1119,28 @@ int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, int peernu
1108 return -1; 1119 return -1;
1109 } 1120 }
1110 1121
1111 if ((uint32_t)peernumber >= g->numpeers) { 1122 const Group_Peer *list = frozen ? g->frozen : g->group;
1123 const uint32_t num = frozen ? g->numfrozen : g->numpeers;
1124
1125 if ((uint32_t)peernumber >= num) {
1112 return -2; 1126 return -2;
1113 } 1127 }
1114 1128
1115 if (g->group[peernumber].nick_len == 0) { 1129 if (list[peernumber].nick_len == 0) {
1116 return 0; 1130 return 0;
1117 } 1131 }
1118 1132
1119 return g->group[peernumber].nick_len; 1133 return list[peernumber].nick_len;
1120} 1134}
1121 1135
1122/* Copy the name of peernumber who is in groupnumber to name. 1136/* Copy the name of (frozen, if frozen is true) peernumber who is in
1123 * name must be at least MAX_NAME_LENGTH long. 1137 * groupnumber to name. name must be at least MAX_NAME_LENGTH long.
1124 * 1138 *
1125 * return length of name if success 1139 * return length of name if success
1126 * return -1 if groupnumber is invalid. 1140 * return -1 if groupnumber is invalid.
1127 * return -2 if peernumber is invalid. 1141 * return -2 if peernumber is invalid.
1128 */ 1142 */
1129int group_peername(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *name) 1143int group_peername(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *name, bool frozen)
1130{ 1144{
1131 Group_c *g = get_group_c(g_c, groupnumber); 1145 Group_c *g = get_group_c(g_c, groupnumber);
1132 1146
@@ -1134,19 +1148,46 @@ int group_peername(const Group_Chats *g_c, uint32_t groupnumber, int peernumber,
1134 return -1; 1148 return -1;
1135 } 1149 }
1136 1150
1137 if ((uint32_t)peernumber >= g->numpeers) { 1151 const Group_Peer *list = frozen ? g->frozen : g->group;
1152 const uint32_t num = frozen ? g->numfrozen : g->numpeers;
1153
1154 if ((uint32_t)peernumber >= num) {
1138 return -2; 1155 return -2;
1139 } 1156 }
1140 1157
1141 if (g->group[peernumber].nick_len == 0) { 1158 if (list[peernumber].nick_len == 0) {
1142 return 0; 1159 return 0;
1143 } 1160 }
1144 1161
1145 memcpy(name, g->group[peernumber].nick, g->group[peernumber].nick_len); 1162 memcpy(name, list[peernumber].nick, list[peernumber].nick_len);
1146 return g->group[peernumber].nick_len; 1163 return list[peernumber].nick_len;
1147} 1164}
1148 1165
1149/* List all the peers in the group chat. 1166/* Copy last active timestamp of frozennumber who is in groupnumber to
1167 * last_active.
1168 *
1169 * return 0 on success.
1170 * return -1 if groupnumber is invalid.
1171 * return -2 if frozennumber is invalid.
1172 */
1173int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, int peernumber,
1174 uint64_t *last_active)
1175{
1176 Group_c *g = get_group_c(g_c, groupnumber);
1177
1178 if (!g) {
1179 return -1;
1180 }
1181
1182 if ((uint32_t)peernumber >= g->numfrozen) {
1183 return -2;
1184 }
1185
1186 *last_active = g->frozen[peernumber].last_active;
1187 return 0;
1188}
1189
1190/* List all the (frozen, if frozen is true) peers in the group chat.
1150 * 1191 *
1151 * Copies the names of the peers to the name[length][MAX_NAME_LENGTH] array. 1192 * Copies the names of the peers to the name[length][MAX_NAME_LENGTH] array.
1152 * 1193 *
@@ -1157,7 +1198,7 @@ int group_peername(const Group_Chats *g_c, uint32_t groupnumber, int peernumber,
1157 * return -1 on failure. 1198 * return -1 on failure.
1158 */ 1199 */
1159int group_names(const Group_Chats *g_c, uint32_t groupnumber, uint8_t names[][MAX_NAME_LENGTH], uint16_t lengths[], 1200int group_names(const Group_Chats *g_c, uint32_t groupnumber, uint8_t names[][MAX_NAME_LENGTH], uint16_t lengths[],
1160 uint16_t length) 1201 uint16_t length, bool frozen)
1161{ 1202{
1162 Group_c *g = get_group_c(g_c, groupnumber); 1203 Group_c *g = get_group_c(g_c, groupnumber);
1163 1204
@@ -1165,19 +1206,22 @@ int group_names(const Group_Chats *g_c, uint32_t groupnumber, uint8_t names[][MA
1165 return -1; 1206 return -1;
1166 } 1207 }
1167 1208
1209 const uint32_t num = frozen ? g->numfrozen : g->numpeers;
1210
1168 unsigned int i; 1211 unsigned int i;
1169 1212
1170 for (i = 0; i < g->numpeers && i < length; ++i) { 1213 for (i = 0; i < num && i < length; ++i) {
1171 lengths[i] = group_peername(g_c, groupnumber, i, names[i]); 1214 lengths[i] = group_peername(g_c, groupnumber, i, names[i], frozen);
1172 } 1215 }
1173 1216
1174 return i; 1217 return i;
1175} 1218}
1176 1219
1177/* Return the number of peers in the group chat on success. 1220/* Return the number of (frozen, if frozen is true) peers in the group chat on
1221 * success.
1178 * return -1 if groupnumber is invalid. 1222 * return -1 if groupnumber is invalid.
1179 */ 1223 */
1180int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber) 1224int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber, bool frozen)
1181{ 1225{
1182 Group_c *g = get_group_c(g_c, groupnumber); 1226 Group_c *g = get_group_c(g_c, groupnumber);
1183 1227
@@ -1185,7 +1229,7 @@ int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber)
1185 return -1; 1229 return -1;
1186 } 1230 }
1187 1231
1188 return g->numpeers; 1232 return frozen ? g->numfrozen : g->numpeers;
1189} 1233}
1190 1234
1191/* return 1 if the peernumber corresponds to ours. 1235/* return 1 if the peernumber corresponds to ours.
@@ -1597,18 +1641,18 @@ static int group_name_send(const Group_Chats *g_c, uint32_t groupnumber, const u
1597} 1641}
1598 1642
1599/* send message to announce leaving group 1643/* send message to announce leaving group
1600 * return true on success 1644 * return 0 on success
1601 * return false on failure 1645 * return -1 on failure
1602 */ 1646 */
1603bool group_leave(const Group_Chats *g_c, uint32_t groupnumber) 1647static int group_leave(const Group_Chats *g_c, uint32_t groupnumber)
1604{ 1648{
1605 Group_c *g = get_group_c(g_c, groupnumber); 1649 Group_c *g = get_group_c(g_c, groupnumber);
1606 1650
1607 if (!g) { 1651 if (!g) {
1608 return false; 1652 return -1;
1609 } 1653 }
1610 1654
1611 return group_kill_peer_send(g_c, groupnumber, g->peer_number) == 0; 1655 return group_kill_peer_send(g_c, groupnumber, g->peer_number);
1612} 1656}
1613 1657
1614 1658
@@ -2900,7 +2944,7 @@ void send_name_all_groups(Group_Chats *g_c)
2900 } 2944 }
2901} 2945}
2902 2946
2903#define SAVED_PEER_SIZE_CONSTANT (2 * CRYPTO_PUBLIC_KEY_SIZE + 2 + 1) 2947#define SAVED_PEER_SIZE_CONSTANT (2 * CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint16_t) + sizeof(uint64_t) + 1)
2904 2948
2905static uint32_t saved_peer_size(const Group_Peer *peer) 2949static uint32_t saved_peer_size(const Group_Peer *peer)
2906{ 2950{
@@ -2918,6 +2962,9 @@ static uint8_t *save_peer(const Group_Peer *peer, uint8_t *data)
2918 host_to_lendian_bytes16(data, peer->peer_number); 2962 host_to_lendian_bytes16(data, peer->peer_number);
2919 data += sizeof(uint16_t); 2963 data += sizeof(uint16_t);
2920 2964
2965 host_to_lendian_bytes64(data, peer->last_active);
2966 data += sizeof(uint64_t);
2967
2921 *data = peer->nick_len; 2968 *data = peer->nick_len;
2922 ++data; 2969 ++data;
2923 2970
@@ -3098,6 +3145,9 @@ static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data,
3098 lendian_bytes_to_host16(&peer->peer_number, data); 3145 lendian_bytes_to_host16(&peer->peer_number, data);
3099 data += sizeof(uint16_t); 3146 data += sizeof(uint16_t);
3100 3147
3148 lendian_bytes_to_host64(&peer->last_active, data);
3149 data += sizeof(uint64_t);
3150
3101 peer->nick_len = *data; 3151 peer->nick_len = *data;
3102 ++data; 3152 ++data;
3103 3153
@@ -3189,7 +3239,7 @@ void do_groupchats(Group_Chats *g_c, void *userdata)
3189void kill_groupchats(Group_Chats *g_c) 3239void kill_groupchats(Group_Chats *g_c)
3190{ 3240{
3191 for (uint16_t i = 0; i < g_c->num_chats; ++i) { 3241 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3192 del_groupchat(g_c, i); 3242 del_groupchat(g_c, i, false);
3193 } 3243 }
3194 3244
3195 m_callback_conference_invite(g_c->m, nullptr); 3245 m_callback_conference_invite(g_c->m, nullptr);
diff --git a/toxcore/group.h b/toxcore/group.h
index 148de0c1..83015273 100644
--- a/toxcore/group.h
+++ b/toxcore/group.h
@@ -238,38 +238,51 @@ void g_callback_peer_list_changed(Group_Chats *g_c, peer_list_changed_cb *functi
238 */ 238 */
239int add_groupchat(Group_Chats *g_c, uint8_t type); 239int add_groupchat(Group_Chats *g_c, uint8_t type);
240 240
241/* Delete a groupchat from the chats array. 241/* Delete a groupchat from the chats array, informing the group first as
242 * appropriate.
242 * 243 *
243 * return 0 on success. 244 * return 0 on success.
244 * return -1 if groupnumber is invalid. 245 * return -1 if groupnumber is invalid.
245 */ 246 */
246int del_groupchat(Group_Chats *g_c, uint32_t groupnumber); 247int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently);
247 248
248/* Copy the public key of peernumber who is in groupnumber to pk. 249/* Copy the public key of (frozen, if frozen is true) peernumber who is in
250 * groupnumber to pk.
249 * pk must be CRYPTO_PUBLIC_KEY_SIZE long. 251 * pk must be CRYPTO_PUBLIC_KEY_SIZE long.
250 * 252 *
251 * return 0 on success 253 * return 0 on success
252 * return -1 if groupnumber is invalid. 254 * return -1 if groupnumber is invalid.
253 * return -2 if peernumber is invalid. 255 * return -2 if peernumber is invalid.
254 */ 256 */
255int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *pk); 257int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *pk, bool frozen);
256 258
257/* 259/*
258 * Return the size of peernumber's name. 260 * Return the size of (frozen, if frozen is true) peernumber's name.
259 * 261 *
260 * return -1 if groupnumber is invalid. 262 * return -1 if groupnumber is invalid.
261 * return -2 if peernumber is invalid. 263 * return -2 if peernumber is invalid.
262 */ 264 */
263int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, int32_t peernumber); 265int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, int32_t peernumber, bool frozen);
264 266
265/* Copy the name of peernumber who is in groupnumber to name. 267/* Copy the name of (frozen, if frozen is true) peernumber who is in
268 * groupnumber to name.
266 * name must be at least MAX_NAME_LENGTH long. 269 * name must be at least MAX_NAME_LENGTH long.
267 * 270 *
268 * return length of name if success 271 * return length of name if success
269 * return -1 if groupnumber is invalid. 272 * return -1 if groupnumber is invalid.
270 * return -2 if peernumber is invalid. 273 * return -2 if peernumber is invalid.
271 */ 274 */
272int group_peername(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *name); 275int group_peername(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *name, bool frozen);
276
277/* Copy last active timestamp of frozen peernumber who is in groupnumber to
278 * last_active.
279 *
280 * return 0 on success.
281 * return -1 if groupnumber is invalid.
282 * return -2 if peernumber is invalid.
283 */
284int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, int peernumber,
285 uint64_t *last_active);
273 286
274/* invite friendnumber to groupnumber 287/* invite friendnumber to groupnumber
275 * 288 *
@@ -306,12 +319,6 @@ int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8
306 */ 319 */
307int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length); 320int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length);
308 321
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
315/* set the group's title, limited to MAX_NAME_LENGTH 322/* set the group's title, limited to MAX_NAME_LENGTH
316 * return 0 on success 323 * return 0 on success
317 * return -1 if groupnumber is invalid. 324 * return -1 if groupnumber is invalid.
@@ -336,10 +343,11 @@ int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber);
336 */ 343 */
337int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title); 344int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title);
338 345
339/* Return the number of peers in the group chat on success. 346/* Return the number of (frozen, if frozen is true) peers in the group chat on
347 * success.
340 * return -1 if groupnumber is invalid. 348 * return -1 if groupnumber is invalid.
341 */ 349 */
342int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber); 350int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber, bool frozen);
343 351
344/* return 1 if the peernumber corresponds to ours. 352/* return 1 if the peernumber corresponds to ours.
345 * return 0 if the peernumber is not ours. 353 * return 0 if the peernumber is not ours.
@@ -349,7 +357,7 @@ int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber);
349 */ 357 */
350int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, int peernumber); 358int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, int peernumber);
351 359
352/* List all the peers in the group chat. 360/* List all the (frozen, if frozen is true) peers in the group chat.
353 * 361 *
354 * Copies the names of the peers to the name[length][MAX_NAME_LENGTH] array. 362 * Copies the names of the peers to the name[length][MAX_NAME_LENGTH] array.
355 * 363 *
@@ -360,7 +368,7 @@ int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, int p
360 * return -1 on failure. 368 * return -1 on failure.
361 */ 369 */
362int group_names(const Group_Chats *g_c, uint32_t groupnumber, uint8_t names[][MAX_NAME_LENGTH], uint16_t lengths[], 370int group_names(const Group_Chats *g_c, uint32_t groupnumber, uint8_t names[][MAX_NAME_LENGTH], uint16_t lengths[],
363 uint16_t length); 371 uint16_t length, bool frozen);
364 372
365/* Set handlers for custom lossy packets. */ 373/* Set handlers for custom lossy packets. */
366void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, lossy_packet_cb *function); 374void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, lossy_packet_cb *function);
diff --git a/toxcore/state.c b/toxcore/state.c
index 919c2e8c..bca7a151 100644
--- a/toxcore/state.c
+++ b/toxcore/state.c
@@ -84,6 +84,28 @@ uint16_t host_to_lendian16(uint16_t host)
84 return lendian_to_host16(host); 84 return lendian_to_host16(host);
85} 85}
86 86
87void host_to_lendian_bytes64(uint8_t *dest, uint64_t num)
88{
89#ifdef WORDS_BIGENDIAN
90 num = ((num << 8) & 0xFF00FF00FF00FF00) | ((num >> 8) & 0xFF00FF00FF00FF);
91 num = ((num << 16) & 0xFFFF0000FFFF0000) | ((num >> 16) & 0xFFFF0000FFFF);
92 num = (num << 32) | (num >> 32);
93#endif
94 memcpy(dest, &num, sizeof(uint64_t));
95}
96
97void lendian_bytes_to_host64(uint64_t *dest, const uint8_t *lendian)
98{
99 uint64_t d;
100 memcpy(&d, lendian, sizeof(uint64_t));
101#ifdef WORDS_BIGENDIAN
102 d = ((d << 8) & 0xFF00FF00FF00FF00) | ((d >> 8) & 0xFF00FF00FF00FF);
103 d = ((d << 16) & 0xFFFF0000FFFF0000) | ((d >> 16) & 0xFFFF0000FFFF);
104 d = (d << 32) | (d >> 32);
105#endif
106 *dest = d;
107}
108
87void host_to_lendian_bytes32(uint8_t *dest, uint32_t num) 109void host_to_lendian_bytes32(uint8_t *dest, uint32_t num)
88{ 110{
89#ifdef WORDS_BIGENDIAN 111#ifdef WORDS_BIGENDIAN
diff --git a/toxcore/state.h b/toxcore/state.h
index 6e3c897e..829036d8 100644
--- a/toxcore/state.h
+++ b/toxcore/state.h
@@ -58,6 +58,9 @@ uint8_t *state_write_section_header(uint8_t *data, uint16_t cookie_type, uint32_
58uint16_t lendian_to_host16(uint16_t lendian); 58uint16_t lendian_to_host16(uint16_t lendian);
59uint16_t host_to_lendian16(uint16_t host); 59uint16_t host_to_lendian16(uint16_t host);
60 60
61void host_to_lendian_bytes64(uint8_t *dest, uint64_t num);
62void lendian_bytes_to_host64(uint64_t *dest, const uint8_t *lendian);
63
61void host_to_lendian_bytes32(uint8_t *dest, uint32_t num); 64void host_to_lendian_bytes32(uint8_t *dest, uint32_t num);
62void lendian_bytes_to_host32(uint32_t *dest, const uint8_t *lendian); 65void lendian_bytes_to_host32(uint32_t *dest, const uint8_t *lendian);
63 66
diff --git a/toxcore/tox.api.h b/toxcore/tox.api.h
index 53788473..ba1932f4 100644
--- a/toxcore/tox.api.h
+++ b/toxcore/tox.api.h
@@ -2257,32 +2257,32 @@ namespace conference {
2257 CONFERENCE_NOT_FOUND, 2257 CONFERENCE_NOT_FOUND,
2258 } 2258 }
2259 2259
2260 2260 /**
2261 namespace peer { 2261 * Error codes for peer info queries.
2262 2262 */
2263 error for peer_query {
2263 /** 2264 /**
2264 * Error codes for peer info queries. 2265 * The conference number passed did not designate a valid conference.
2265 */ 2266 */
2266 error for query { 2267 CONFERENCE_NOT_FOUND,
2267 /** 2268 /**
2268 * The conference number passed did not designate a valid conference. 2269 * The peer number passed did not designate a valid peer.
2269 */ 2270 */
2270 CONFERENCE_NOT_FOUND, 2271 PEER_NOT_FOUND,
2271 /** 2272 /**
2272 * The peer number passed did not designate a valid peer. 2273 * The client is not connected to the conference.
2273 */ 2274 */
2274 PEER_NOT_FOUND, 2275 NO_CONNECTION,
2275 /** 2276 }
2276 * The client is not connected to the conference. 2277
2277 */ 2278
2278 NO_CONNECTION, 2279 namespace peer {
2279 }
2280 2280
2281 /** 2281 /**
2282 * Return the number of peers in the conference. Return value is unspecified on failure. 2282 * Return the number of peers in the conference. Return value is unspecified on failure.
2283 */ 2283 */
2284 const uint32_t count(uint32_t conference_number) 2284 const uint32_t count(uint32_t conference_number)
2285 with error for query; 2285 with error for peer_query;
2286 2286
2287 uint8_t[size] name { 2287 uint8_t[size] name {
2288 2288
@@ -2290,7 +2290,7 @@ namespace conference {
2290 * Return the length of the peer's name. Return value is unspecified on failure. 2290 * Return the length of the peer's name. Return value is unspecified on failure.
2291 */ 2291 */
2292 size(uint32_t conference_number, uint32_t peer_number) 2292 size(uint32_t conference_number, uint32_t peer_number)
2293 with error for query; 2293 with error for peer_query;
2294 2294
2295 /** 2295 /**
2296 * Copy the name of peer_number who is in conference_number to name. 2296 * Copy the name of peer_number who is in conference_number to name.
@@ -2299,7 +2299,7 @@ namespace conference {
2299 * @return true on success. 2299 * @return true on success.
2300 */ 2300 */
2301 get(uint32_t conference_number, uint32_t peer_number) 2301 get(uint32_t conference_number, uint32_t peer_number)
2302 with error for query; 2302 with error for peer_query;
2303 } 2303 }
2304 2304
2305 /** 2305 /**
@@ -2310,17 +2310,63 @@ namespace conference {
2310 */ 2310 */
2311 uint8_t[PUBLIC_KEY_SIZE] public_key { 2311 uint8_t[PUBLIC_KEY_SIZE] public_key {
2312 get(uint32_t conference_number, uint32_t peer_number) 2312 get(uint32_t conference_number, uint32_t peer_number)
2313 with error for query; 2313 with error for peer_query;
2314 } 2314 }
2315 2315
2316 /** 2316 /**
2317 * Return true if passed peer_number corresponds to our own. 2317 * Return true if passed peer_number corresponds to our own.
2318 */ 2318 */
2319 const bool number_is_ours(uint32_t conference_number, uint32_t peer_number) 2319 const bool number_is_ours(uint32_t conference_number, uint32_t peer_number)
2320 with error for query; 2320 with error for peer_query;
2321 2321
2322 } 2322 }
2323 2323
2324 namespace offline_peer {
2325
2326 /**
2327 * Return the number of offline peers in the conference. Return value is unspecified on failure.
2328 */
2329 const uint32_t count(uint32_t conference_number)
2330 with error for peer_query;
2331
2332 uint8_t[size] name {
2333
2334 /**
2335 * Return the length of the offline peer's name. Return value is unspecified on failure.
2336 */
2337 size(uint32_t conference_number, uint32_t offline_peer_number)
2338 with error for peer_query;
2339
2340 /**
2341 * Copy the name of offline_peer_number who is in conference_number to name.
2342 * name must be at least $MAX_NAME_LENGTH long.
2343 *
2344 * @return true on success.
2345 */
2346 get(uint32_t conference_number, uint32_t offline_peer_number)
2347 with error for peer_query;
2348 }
2349
2350 /**
2351 * Copy the public key of offline_peer_number who is in conference_number to public_key.
2352 * public_key must be $PUBLIC_KEY_SIZE long.
2353 *
2354 * @return true on success.
2355 */
2356 uint8_t[PUBLIC_KEY_SIZE] public_key {
2357 get(uint32_t conference_number, uint32_t offline_peer_number)
2358 with error for peer_query;
2359 }
2360
2361 /**
2362 * Return a unix-time timestamp of the last time offline_peer_number was seen to be active.
2363 */
2364 uint64_t last_active {
2365 get(uint32_t conference_number, uint32_t offline_peer_number)
2366 with error for peer_query;
2367 }
2368
2369 }
2324 2370
2325 /** 2371 /**
2326 * Invites a friend to a conference. 2372 * Invites a friend to a conference.
diff --git a/toxcore/tox.c b/toxcore/tox.c
index 88a9bd50..d848102e 100644
--- a/toxcore/tox.c
+++ b/toxcore/tox.c
@@ -713,6 +713,7 @@ Tox_Connection tox_self_get_connection_status(const Tox *tox)
713{ 713{
714 const Messenger *m = tox->m; 714 const Messenger *m = tox->m;
715 715
716 // TODO(zugz): this can be const. Similarly throughout the file.
716 unsigned int ret = onion_connection_status(m->onion_c); 717 unsigned int ret = onion_connection_status(m->onion_c);
717 718
718 if (ret == 2) { 719 if (ret == 2) {
@@ -1533,8 +1534,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) 1534bool tox_conference_delete(Tox *tox, uint32_t conference_number, Tox_Err_Conference_Delete *error)
1534{ 1535{
1535 Messenger *m = tox->m; 1536 Messenger *m = tox->m;
1536 group_leave(m->conferences_object, conference_number); 1537 int ret = del_groupchat(m->conferences_object, conference_number, true);
1537 int ret = del_groupchat(m->conferences_object, conference_number);
1538 1538
1539 if (ret == -1) { 1539 if (ret == -1) {
1540 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_DELETE_CONFERENCE_NOT_FOUND); 1540 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_DELETE_CONFERENCE_NOT_FOUND);
@@ -1548,7 +1548,7 @@ bool tox_conference_delete(Tox *tox, uint32_t conference_number, Tox_Err_Confere
1548uint32_t tox_conference_peer_count(const Tox *tox, uint32_t conference_number, Tox_Err_Conference_Peer_Query *error) 1548uint32_t tox_conference_peer_count(const Tox *tox, uint32_t conference_number, Tox_Err_Conference_Peer_Query *error)
1549{ 1549{
1550 const Messenger *m = tox->m; 1550 const Messenger *m = tox->m;
1551 int ret = group_number_peers(m->conferences_object, conference_number); 1551 int ret = group_number_peers(m->conferences_object, conference_number, false);
1552 1552
1553 if (ret == -1) { 1553 if (ret == -1) {
1554 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND); 1554 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
@@ -1563,7 +1563,7 @@ size_t tox_conference_peer_get_name_size(const Tox *tox, uint32_t conference_num
1563 Tox_Err_Conference_Peer_Query *error) 1563 Tox_Err_Conference_Peer_Query *error)
1564{ 1564{
1565 const Messenger *m = tox->m; 1565 const Messenger *m = tox->m;
1566 int ret = group_peername_size(m->conferences_object, conference_number, peer_number); 1566 int ret = group_peername_size(m->conferences_object, conference_number, peer_number, false);
1567 1567
1568 switch (ret) { 1568 switch (ret) {
1569 case -1: 1569 case -1:
@@ -1583,7 +1583,7 @@ bool tox_conference_peer_get_name(const Tox *tox, uint32_t conference_number, ui
1583 Tox_Err_Conference_Peer_Query *error) 1583 Tox_Err_Conference_Peer_Query *error)
1584{ 1584{
1585 const Messenger *m = tox->m; 1585 const Messenger *m = tox->m;
1586 int ret = group_peername(m->conferences_object, conference_number, peer_number, name); 1586 int ret = group_peername(m->conferences_object, conference_number, peer_number, name, false);
1587 1587
1588 switch (ret) { 1588 switch (ret) {
1589 case -1: 1589 case -1:
@@ -1603,7 +1603,7 @@ bool tox_conference_peer_get_public_key(const Tox *tox, uint32_t conference_numb
1603 uint8_t *public_key, Tox_Err_Conference_Peer_Query *error) 1603 uint8_t *public_key, Tox_Err_Conference_Peer_Query *error)
1604{ 1604{
1605 const Messenger *m = tox->m; 1605 const Messenger *m = tox->m;
1606 int ret = group_peer_pubkey(m->conferences_object, conference_number, peer_number, public_key); 1606 int ret = group_peer_pubkey(m->conferences_object, conference_number, peer_number, public_key, false);
1607 1607
1608 switch (ret) { 1608 switch (ret) {
1609 case -1: 1609 case -1:
@@ -1643,6 +1643,106 @@ bool tox_conference_peer_number_is_ours(const Tox *tox, uint32_t conference_numb
1643 return ret; 1643 return ret;
1644} 1644}
1645 1645
1646uint32_t tox_conference_offline_peer_count(const Tox *tox, uint32_t conference_number,
1647 Tox_Err_Conference_Peer_Query *error)
1648{
1649 const Messenger *m = tox->m;
1650 int ret = group_number_peers(m->conferences_object, conference_number, true);
1651
1652 if (ret == -1) {
1653 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
1654 return UINT32_MAX;
1655 }
1656
1657 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
1658 return ret;
1659}
1660
1661size_t tox_conference_offline_peer_get_name_size(const Tox *tox, uint32_t conference_number,
1662 uint32_t offline_peer_number,
1663 Tox_Err_Conference_Peer_Query *error)
1664{
1665 const Messenger *m = tox->m;
1666 int ret = group_peername_size(m->conferences_object, conference_number, offline_peer_number, true);
1667
1668 switch (ret) {
1669 case -1:
1670 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
1671 return -1;
1672
1673 case -2:
1674 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
1675 return -1;
1676 }
1677
1678 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
1679 return ret;
1680}
1681
1682bool tox_conference_offline_peer_get_name(const Tox *tox, uint32_t conference_number, uint32_t offline_peer_number,
1683 uint8_t *name,
1684 Tox_Err_Conference_Peer_Query *error)
1685{
1686 const Messenger *m = tox->m;
1687 int ret = group_peername(m->conferences_object, conference_number, offline_peer_number, name, true);
1688
1689 switch (ret) {
1690 case -1:
1691 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
1692 return false;
1693
1694 case -2:
1695 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
1696 return false;
1697 }
1698
1699 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
1700 return true;
1701}
1702
1703bool tox_conference_offline_peer_get_public_key(const Tox *tox, uint32_t conference_number,
1704 uint32_t offline_peer_number,
1705 uint8_t *public_key, Tox_Err_Conference_Peer_Query *error)
1706{
1707 const Messenger *m = tox->m;
1708 int ret = group_peer_pubkey(m->conferences_object, conference_number, offline_peer_number, public_key, true);
1709
1710 switch (ret) {
1711 case -1:
1712 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
1713 return false;
1714
1715 case -2:
1716 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
1717 return false;
1718 }
1719
1720 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
1721 return true;
1722}
1723
1724uint64_t tox_conference_offline_peer_get_last_active(const Tox *tox, uint32_t conference_number,
1725 uint32_t offline_peer_number,
1726 Tox_Err_Conference_Peer_Query *error)
1727{
1728 const Messenger *m = tox->m;
1729 uint64_t last_active = UINT64_MAX;
1730 int ret = group_frozen_last_active(m->conferences_object, conference_number, offline_peer_number, &last_active);
1731
1732 switch (ret) {
1733 case -1:
1734 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
1735 return UINT64_MAX;
1736
1737 case -2:
1738 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
1739 return UINT64_MAX;
1740 }
1741
1742 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
1743 return last_active;
1744}
1745
1646bool tox_conference_invite(Tox *tox, uint32_t friend_number, uint32_t conference_number, 1746bool tox_conference_invite(Tox *tox, uint32_t friend_number, uint32_t conference_number,
1647 Tox_Err_Conference_Invite *error) 1747 Tox_Err_Conference_Invite *error)
1648{ 1748{
diff --git a/toxcore/tox.h b/toxcore/tox.h
index ee8a01cc..94ffd32a 100644
--- a/toxcore/tox.h
+++ b/toxcore/tox.h
@@ -2631,6 +2631,42 @@ bool tox_conference_peer_get_public_key(const Tox *tox, uint32_t conference_numb
2631bool tox_conference_peer_number_is_ours(const Tox *tox, uint32_t conference_number, uint32_t peer_number, 2631bool tox_conference_peer_number_is_ours(const Tox *tox, uint32_t conference_number, uint32_t peer_number,
2632 TOX_ERR_CONFERENCE_PEER_QUERY *error); 2632 TOX_ERR_CONFERENCE_PEER_QUERY *error);
2633 2633
2634/**
2635 * Return the number of offline peers in the conference. Return value is unspecified on failure.
2636 */
2637uint32_t tox_conference_offline_peer_count(const Tox *tox, uint32_t conference_number,
2638 TOX_ERR_CONFERENCE_PEER_QUERY *error);
2639
2640/**
2641 * Return the length of the offline peer's name. Return value is unspecified on failure.
2642 */
2643size_t tox_conference_offline_peer_get_name_size(const Tox *tox, uint32_t conference_number,
2644 uint32_t offline_peer_number, TOX_ERR_CONFERENCE_PEER_QUERY *error);
2645
2646/**
2647 * Copy the name of offline_peer_number who is in conference_number to name.
2648 * name must be at least TOX_MAX_NAME_LENGTH long.
2649 *
2650 * @return true on success.
2651 */
2652bool tox_conference_offline_peer_get_name(const Tox *tox, uint32_t conference_number, uint32_t offline_peer_number,
2653 uint8_t *name, TOX_ERR_CONFERENCE_PEER_QUERY *error);
2654
2655/**
2656 * Copy the public key of offline_peer_number who is in conference_number to public_key.
2657 * public_key must be TOX_PUBLIC_KEY_SIZE long.
2658 *
2659 * @return true on success.
2660 */
2661bool tox_conference_offline_peer_get_public_key(const Tox *tox, uint32_t conference_number,
2662 uint32_t offline_peer_number, uint8_t *public_key, TOX_ERR_CONFERENCE_PEER_QUERY *error);
2663
2664/**
2665 * Return a unix-time timestamp of the last time offline_peer_number was seen to be active.
2666 */
2667uint64_t tox_conference_offline_peer_get_last_active(const Tox *tox, uint32_t conference_number,
2668 uint32_t offline_peer_number, TOX_ERR_CONFERENCE_PEER_QUERY *error);
2669
2634typedef enum TOX_ERR_CONFERENCE_INVITE { 2670typedef enum TOX_ERR_CONFERENCE_INVITE {
2635 2671
2636 /** 2672 /**