/work/toxcore/group_pack.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2020 The TokTok team. |
3 | | * Copyright © 2015 Tox project. |
4 | | */ |
5 | | |
6 | | /** |
7 | | * Packer and unpacker functions for saving and loading groups. |
8 | | */ |
9 | | |
10 | | #include "group_pack.h" |
11 | | |
12 | | #include <stdint.h> |
13 | | #include <string.h> |
14 | | |
15 | | #include "DHT.h" |
16 | | #include "attributes.h" |
17 | | #include "bin_pack.h" |
18 | | #include "bin_unpack.h" |
19 | | #include "ccompat.h" |
20 | | #include "crypto_core.h" |
21 | | #include "crypto_core_pack.h" |
22 | | #include "group_common.h" |
23 | | #include "group_moderation.h" |
24 | | #include "logger.h" |
25 | | #include "mem.h" |
26 | | #include "network.h" |
27 | | #include "util.h" |
28 | | |
29 | | static_assert(GC_SAVED_PEER_SIZE >= sizeof(IP_Port), |
30 | | "GC_SAVED_PEER_SIZE cannot contain IP_Port"); |
31 | | |
32 | | bool group_privacy_state_from_int(uint8_t value, Group_Privacy_State *out_enum) |
33 | 4.77k | { |
34 | 4.77k | switch (value) { |
35 | 4.39k | case GI_PUBLIC: { |
36 | 4.39k | *out_enum = GI_PUBLIC; |
37 | 4.39k | return true; |
38 | 0 | } |
39 | | |
40 | 274 | case GI_PRIVATE: { |
41 | 274 | *out_enum = GI_PRIVATE; |
42 | 274 | return true; |
43 | 0 | } |
44 | | |
45 | 103 | default: { |
46 | 103 | *out_enum = GI_PUBLIC; |
47 | 103 | return false; |
48 | 0 | } |
49 | 4.77k | } |
50 | 4.77k | } |
51 | | |
52 | | bool group_voice_state_from_int(uint8_t value, Group_Voice_State *out_enum) |
53 | 4.77k | { |
54 | 4.77k | switch (value) { |
55 | 2.46k | case GV_ALL: { |
56 | 2.46k | *out_enum = GV_ALL; |
57 | 2.46k | return true; |
58 | 0 | } |
59 | | |
60 | 19 | case GV_MODS: { |
61 | 19 | *out_enum = GV_MODS; |
62 | 19 | return true; |
63 | 0 | } |
64 | | |
65 | 8 | case GV_FOUNDER: { |
66 | 8 | *out_enum = GV_FOUNDER; |
67 | 8 | return true; |
68 | 0 | } |
69 | | |
70 | 2.27k | default: { |
71 | 2.27k | *out_enum = GV_ALL; |
72 | 2.27k | return false; |
73 | 0 | } |
74 | 4.77k | } |
75 | 4.77k | } |
76 | | |
77 | | static bool load_unpack_state_values(GC_Chat *_Nonnull chat, Bin_Unpack *_Nonnull bu) |
78 | 18.5k | { |
79 | 18.5k | if (!bin_unpack_array_fixed(bu, 8, nullptr)) { |
80 | 1.31k | LOGGER_ERROR(chat->log, "Group state values array malformed"); |
81 | 1.31k | return false; |
82 | 1.31k | } |
83 | | |
84 | 17.2k | bool manually_disconnected = false; |
85 | 17.2k | uint8_t privacy_state = 0; |
86 | 17.2k | uint8_t voice_state = 0; |
87 | | |
88 | 17.2k | if (!(bin_unpack_bool(bu, &manually_disconnected) |
89 | 17.2k | && bin_unpack_u16(bu, &chat->shared_state.group_name_len) |
90 | 17.2k | && bin_unpack_u08(bu, &privacy_state) |
91 | 17.2k | && bin_unpack_u16(bu, &chat->shared_state.maxpeers) |
92 | 17.2k | && bin_unpack_u16(bu, &chat->shared_state.password_length) |
93 | 17.2k | && bin_unpack_u32(bu, &chat->shared_state.version) |
94 | 17.2k | && bin_unpack_u32(bu, &chat->shared_state.topic_lock) |
95 | 17.2k | && bin_unpack_u08(bu, &voice_state))) { |
96 | 12.9k | LOGGER_ERROR(chat->log, "Failed to unpack state value"); |
97 | 12.9k | return false; |
98 | 12.9k | } |
99 | | |
100 | 4.37k | chat->connection_state = manually_disconnected ? CS_DISCONNECTED : CS_CONNECTING; |
101 | 4.37k | group_privacy_state_from_int(privacy_state, &chat->shared_state.privacy_state); |
102 | 4.37k | group_voice_state_from_int(voice_state, &chat->shared_state.voice_state); |
103 | | |
104 | | // we always load saved groups as private in case the group became private while we were offline. |
105 | | // this will have no detrimental effect if the group is public, as the correct privacy |
106 | | // state will be set via sync. |
107 | 4.37k | chat->join_type = HJ_PRIVATE; |
108 | | |
109 | 4.37k | return true; |
110 | 17.2k | } |
111 | | |
112 | | static bool load_unpack_state_bin(GC_Chat *_Nonnull chat, Bin_Unpack *_Nonnull bu) |
113 | 4.37k | { |
114 | 4.37k | if (!bin_unpack_array_fixed(bu, 5, nullptr)) { |
115 | 3.74k | LOGGER_ERROR(chat->log, "Group state binary array malformed"); |
116 | 3.74k | return false; |
117 | 3.74k | } |
118 | | |
119 | 631 | if (!bin_unpack_bin_fixed(bu, chat->shared_state_sig, SIGNATURE_SIZE)) { |
120 | 95 | LOGGER_ERROR(chat->log, "Failed to unpack shared state signature"); |
121 | 95 | return false; |
122 | 95 | } |
123 | | |
124 | 536 | if (!unpack_extended_public_key(&chat->shared_state.founder_public_key, bu)) { |
125 | 65 | LOGGER_ERROR(chat->log, "Failed to unpack founder public key"); |
126 | 65 | return false; |
127 | 65 | } |
128 | | |
129 | 471 | if (!(bin_unpack_bin_max(bu, chat->shared_state.group_name, &chat->shared_state.group_name_len, sizeof(chat->shared_state.group_name)) |
130 | 471 | && bin_unpack_bin_max(bu, chat->shared_state.password, &chat->shared_state.password_length, sizeof(chat->shared_state.password)) |
131 | 471 | && bin_unpack_bin_fixed(bu, chat->shared_state.mod_list_hash, MOD_MODERATION_HASH_SIZE))) { |
132 | 77 | LOGGER_ERROR(chat->log, "Failed to unpack state binary data"); |
133 | 77 | return false; |
134 | 77 | } |
135 | | |
136 | 394 | return true; |
137 | 471 | } |
138 | | |
139 | | static bool load_unpack_topic_info(GC_Chat *_Nonnull chat, Bin_Unpack *_Nonnull bu) |
140 | 394 | { |
141 | 394 | if (!bin_unpack_array_fixed(bu, 6, nullptr)) { |
142 | 10 | LOGGER_ERROR(chat->log, "Group topic array malformed"); |
143 | 10 | return false; |
144 | 10 | } |
145 | | |
146 | 384 | if (!(bin_unpack_u32(bu, &chat->topic_info.version) |
147 | 384 | && bin_unpack_u16(bu, &chat->topic_info.length) |
148 | 384 | && bin_unpack_u16(bu, &chat->topic_info.checksum) |
149 | 384 | && bin_unpack_bin_max(bu, chat->topic_info.topic, &chat->topic_info.length, sizeof(chat->topic_info.topic)) |
150 | 384 | && bin_unpack_bin_fixed(bu, chat->topic_info.public_sig_key, SIG_PUBLIC_KEY_SIZE) |
151 | 384 | && bin_unpack_bin_fixed(bu, chat->topic_sig, SIGNATURE_SIZE))) { |
152 | 63 | LOGGER_ERROR(chat->log, "Failed to unpack topic info"); |
153 | 63 | return false; |
154 | 63 | } |
155 | | |
156 | 321 | return true; |
157 | 384 | } |
158 | | |
159 | | static bool load_unpack_mod_list(GC_Chat *_Nonnull chat, Bin_Unpack *_Nonnull bu) |
160 | 321 | { |
161 | 321 | uint32_t actual_size = 0; |
162 | 321 | if (!bin_unpack_array_fixed(bu, 2, &actual_size)) { |
163 | 10 | LOGGER_ERROR(chat->log, "Group mod list array malformed: %u != 2", actual_size); |
164 | 10 | return false; |
165 | 10 | } |
166 | | |
167 | 311 | if (!bin_unpack_u16(bu, &chat->moderation.num_mods)) { |
168 | 10 | LOGGER_ERROR(chat->log, "Failed to unpack mod list value"); |
169 | 10 | return false; |
170 | 10 | } |
171 | | |
172 | 301 | if (chat->moderation.num_mods == 0) { |
173 | 88 | bin_unpack_nil(bu); |
174 | 88 | return true; |
175 | 88 | } |
176 | | |
177 | 213 | if (chat->moderation.num_mods > MOD_MAX_NUM_MODERATORS) { |
178 | 1 | LOGGER_ERROR(chat->log, "moderation count %u exceeds maximum %u", chat->moderation.num_mods, (unsigned int)MOD_MAX_NUM_MODERATORS); |
179 | 1 | chat->moderation.num_mods = MOD_MAX_NUM_MODERATORS; |
180 | 1 | } |
181 | | |
182 | 213 | uint8_t *packed_mod_list = (uint8_t *)mem_balloc(chat->mem, chat->moderation.num_mods * MOD_LIST_ENTRY_SIZE); |
183 | | |
184 | 213 | if (packed_mod_list == nullptr) { |
185 | 0 | LOGGER_ERROR(chat->log, "Failed to allocate memory for packed mod list"); |
186 | 0 | return false; |
187 | 0 | } |
188 | | |
189 | 213 | const size_t packed_size = chat->moderation.num_mods * MOD_LIST_ENTRY_SIZE; |
190 | | |
191 | 213 | if (!bin_unpack_bin_fixed(bu, packed_mod_list, packed_size)) { |
192 | 10 | LOGGER_ERROR(chat->log, "Failed to unpack mod list binary data"); |
193 | 10 | mem_delete(chat->mem, packed_mod_list); |
194 | 10 | return false; |
195 | 10 | } |
196 | | |
197 | 203 | if (mod_list_unpack(&chat->moderation, packed_mod_list, packed_size, chat->moderation.num_mods) == -1) { |
198 | 0 | LOGGER_ERROR(chat->log, "Failed to unpack mod list info"); |
199 | 0 | mem_delete(chat->mem, packed_mod_list); |
200 | 0 | return false; |
201 | 0 | } |
202 | | |
203 | 203 | mem_delete(chat->mem, packed_mod_list); |
204 | | |
205 | 203 | return true; |
206 | 203 | } |
207 | | |
208 | | static bool load_unpack_keys(GC_Chat *_Nonnull chat, Bin_Unpack *_Nonnull bu) |
209 | 291 | { |
210 | 291 | if (!bin_unpack_array_fixed(bu, 4, nullptr)) { |
211 | 26 | LOGGER_ERROR(chat->log, "Group keys array malformed"); |
212 | 26 | return false; |
213 | 26 | } |
214 | | |
215 | 265 | if (!(unpack_extended_public_key(&chat->chat_public_key, bu) |
216 | 265 | && unpack_extended_secret_key(&chat->chat_secret_key, bu) |
217 | 265 | && unpack_extended_public_key(&chat->self_public_key, bu) |
218 | 265 | && unpack_extended_secret_key(&chat->self_secret_key, bu))) { |
219 | 74 | LOGGER_ERROR(chat->log, "Failed to unpack keys"); |
220 | 74 | return false; |
221 | 74 | } |
222 | | |
223 | 191 | return true; |
224 | 265 | } |
225 | | |
226 | | static bool load_unpack_self_info(GC_Chat *_Nonnull chat, Bin_Unpack *_Nonnull bu) |
227 | 191 | { |
228 | 191 | if (!bin_unpack_array_fixed(bu, 4, nullptr)) { |
229 | 10 | LOGGER_ERROR(chat->log, "Group self info array malformed"); |
230 | 10 | return false; |
231 | 10 | } |
232 | | |
233 | 181 | uint8_t self_nick[MAX_GC_NICK_SIZE]; |
234 | 181 | uint16_t self_nick_len = 0; |
235 | 181 | uint8_t self_role = GR_USER; |
236 | 181 | uint8_t self_status = GS_NONE; |
237 | | |
238 | 181 | if (!(bin_unpack_u16(bu, &self_nick_len) |
239 | 181 | && bin_unpack_u08(bu, &self_role) |
240 | 181 | && bin_unpack_u08(bu, &self_status))) { |
241 | 21 | LOGGER_ERROR(chat->log, "Failed to unpack self values"); |
242 | 21 | return false; |
243 | 21 | } |
244 | | |
245 | 160 | if (self_nick_len > MAX_GC_NICK_SIZE) { |
246 | 1 | LOGGER_ERROR(chat->log, "self_nick too big (%u bytes), truncating to %d", self_nick_len, MAX_GC_NICK_SIZE); |
247 | 1 | self_nick_len = MAX_GC_NICK_SIZE; |
248 | 1 | } |
249 | | |
250 | 160 | if (!bin_unpack_bin_fixed(bu, self_nick, self_nick_len)) { |
251 | 11 | LOGGER_ERROR(chat->log, "Failed to unpack self nick bytes"); |
252 | 11 | return false; |
253 | 11 | } |
254 | | |
255 | | // we have to add ourself before setting self info |
256 | 149 | if (peer_add(chat, nullptr, chat->self_public_key.enc) != 0) { |
257 | 0 | LOGGER_ERROR(chat->log, "Failed to add self to peer list"); |
258 | 0 | return false; |
259 | 0 | } |
260 | | |
261 | 149 | if (chat->numpeers == 0) { |
262 | 0 | LOGGER_ERROR(chat->log, "Failed to unpack self: numpeers should be > 0"); |
263 | 0 | return false; |
264 | 0 | } |
265 | | |
266 | 149 | GC_Peer *self = &chat->group[0]; |
267 | | |
268 | 149 | self->gconn.addr.public_key = chat->self_public_key; |
269 | 149 | memcpy(self->nick, self_nick, self_nick_len); |
270 | 149 | self->nick_length = self_nick_len; |
271 | 149 | self->role = (Group_Role)self_role; |
272 | 149 | self->status = (Group_Peer_Status)self_status; |
273 | 149 | self->gconn.confirmed = true; |
274 | | |
275 | 149 | return true; |
276 | 149 | } |
277 | | |
278 | | static bool load_unpack_saved_peers(GC_Chat *_Nonnull chat, Bin_Unpack *_Nonnull bu) |
279 | 149 | { |
280 | 149 | if (!bin_unpack_array_fixed(bu, 2, nullptr)) { |
281 | 10 | LOGGER_ERROR(chat->log, "Group saved peers array malformed"); |
282 | 10 | return false; |
283 | 10 | } |
284 | | |
285 | | // Saved peers |
286 | 139 | uint16_t saved_peers_size = 0; |
287 | | |
288 | 139 | if (!bin_unpack_u16(bu, &saved_peers_size)) { |
289 | 10 | LOGGER_ERROR(chat->log, "Failed to unpack saved peers value"); |
290 | 10 | return false; |
291 | 10 | } |
292 | | |
293 | 129 | if (saved_peers_size == 0) { |
294 | 2 | bin_unpack_nil(bu); |
295 | 2 | return true; |
296 | 2 | } |
297 | | |
298 | 127 | uint8_t *saved_peers = (uint8_t *)mem_balloc(chat->mem, saved_peers_size * GC_SAVED_PEER_SIZE); |
299 | | |
300 | 127 | if (saved_peers == nullptr) { |
301 | 1 | LOGGER_ERROR(chat->log, "Failed to allocate memory for saved peer list"); |
302 | 1 | return false; |
303 | 1 | } |
304 | | |
305 | 126 | if (!bin_unpack_bin_fixed(bu, saved_peers, saved_peers_size)) { |
306 | 21 | LOGGER_ERROR(chat->log, "Failed to unpack saved peers binary data"); |
307 | 21 | mem_delete(chat->mem, saved_peers); |
308 | 21 | return false; |
309 | 21 | } |
310 | | |
311 | 105 | if (unpack_gc_saved_peers(chat, saved_peers, saved_peers_size) == -1) { |
312 | 94 | LOGGER_ERROR(chat->log, "Failed to unpack saved peers"); // recoverable error |
313 | 94 | } |
314 | | |
315 | 105 | mem_delete(chat->mem, saved_peers); |
316 | | |
317 | 105 | return true; |
318 | 126 | } |
319 | | |
320 | | bool gc_load_unpack_group(GC_Chat *chat, Bin_Unpack *bu) |
321 | 21.1k | { |
322 | 21.1k | uint32_t actual_size; |
323 | 21.1k | if (!bin_unpack_array_fixed(bu, 7, &actual_size)) { |
324 | 2.57k | LOGGER_ERROR(chat->log, "Group info array malformed: %u != 7", actual_size); |
325 | 2.57k | return false; |
326 | 2.57k | } |
327 | | |
328 | 18.5k | return load_unpack_state_values(chat, bu) |
329 | 18.5k | && load_unpack_state_bin(chat, bu) |
330 | 18.5k | && load_unpack_topic_info(chat, bu) |
331 | 18.5k | && load_unpack_mod_list(chat, bu) |
332 | 18.5k | && load_unpack_keys(chat, bu) |
333 | 18.5k | && load_unpack_self_info(chat, bu) |
334 | 18.5k | && load_unpack_saved_peers(chat, bu); |
335 | 21.1k | } |
336 | | |
337 | | static void save_pack_state_values(const GC_Chat *_Nonnull chat, Bin_Pack *_Nonnull bp) |
338 | 445 | { |
339 | 445 | bin_pack_array(bp, 8); |
340 | 445 | bin_pack_bool(bp, chat->connection_state == CS_DISCONNECTED); // 1 |
341 | 445 | bin_pack_u16(bp, chat->shared_state.group_name_len); // 2 |
342 | 445 | bin_pack_u08(bp, chat->shared_state.privacy_state); // 3 |
343 | 445 | bin_pack_u16(bp, chat->shared_state.maxpeers); // 4 |
344 | 445 | bin_pack_u16(bp, chat->shared_state.password_length); // 5 |
345 | 445 | bin_pack_u32(bp, chat->shared_state.version); // 6 |
346 | 445 | bin_pack_u32(bp, chat->shared_state.topic_lock); // 7 |
347 | 445 | bin_pack_u08(bp, chat->shared_state.voice_state); // 8 |
348 | 445 | } |
349 | | |
350 | | static void save_pack_state_bin(const GC_Chat *_Nonnull chat, Bin_Pack *_Nonnull bp) |
351 | 445 | { |
352 | 445 | bin_pack_array(bp, 5); |
353 | | |
354 | 445 | bin_pack_bin(bp, chat->shared_state_sig, SIGNATURE_SIZE); // 1 |
355 | 445 | pack_extended_public_key(&chat->shared_state.founder_public_key, bp); // 2 |
356 | 445 | bin_pack_bin(bp, chat->shared_state.group_name, chat->shared_state.group_name_len); // 3 |
357 | 445 | bin_pack_bin(bp, chat->shared_state.password, chat->shared_state.password_length); // 4 |
358 | 445 | bin_pack_bin(bp, chat->shared_state.mod_list_hash, MOD_MODERATION_HASH_SIZE); // 5 |
359 | 445 | } |
360 | | |
361 | | static void save_pack_topic_info(const GC_Chat *_Nonnull chat, Bin_Pack *_Nonnull bp) |
362 | 445 | { |
363 | 445 | bin_pack_array(bp, 6); |
364 | | |
365 | 445 | bin_pack_u32(bp, chat->topic_info.version); // 1 |
366 | 445 | bin_pack_u16(bp, chat->topic_info.length); // 2 |
367 | 445 | bin_pack_u16(bp, chat->topic_info.checksum); // 3 |
368 | 445 | bin_pack_bin(bp, chat->topic_info.topic, chat->topic_info.length); // 4 |
369 | 445 | bin_pack_bin(bp, chat->topic_info.public_sig_key, SIG_PUBLIC_KEY_SIZE); // 5 |
370 | 445 | bin_pack_bin(bp, chat->topic_sig, SIGNATURE_SIZE); // 6 |
371 | 445 | } |
372 | | |
373 | | static void save_pack_mod_list(const GC_Chat *_Nonnull chat, Bin_Pack *_Nonnull bp) |
374 | 445 | { |
375 | 445 | bin_pack_array(bp, 2); |
376 | | |
377 | 445 | const uint16_t num_mods = min_u16(chat->moderation.num_mods, MOD_MAX_NUM_MODERATORS); |
378 | | |
379 | 445 | if (num_mods == 0) { |
380 | 153 | bin_pack_u16(bp, num_mods); // 1 |
381 | 153 | bin_pack_nil(bp); // 2 |
382 | 153 | return; |
383 | 153 | } |
384 | | |
385 | 292 | uint8_t *packed_mod_list = (uint8_t *)mem_balloc(chat->mem, num_mods * MOD_LIST_ENTRY_SIZE); |
386 | | |
387 | | // we can still recover without the mod list |
388 | 292 | if (packed_mod_list == nullptr) { |
389 | 0 | bin_pack_u16(bp, 0); // 1 |
390 | 0 | bin_pack_nil(bp); // 2 |
391 | 0 | LOGGER_ERROR(chat->log, "Failed to allocate memory for moderation list"); |
392 | 0 | return; |
393 | 0 | } |
394 | | |
395 | 292 | bin_pack_u16(bp, num_mods); // 1 |
396 | | |
397 | 292 | mod_list_pack(&chat->moderation, packed_mod_list); |
398 | | |
399 | 292 | const size_t packed_size = num_mods * MOD_LIST_ENTRY_SIZE; |
400 | | |
401 | 292 | bin_pack_bin(bp, packed_mod_list, packed_size); // 2 |
402 | | |
403 | 292 | mem_delete(chat->mem, packed_mod_list); |
404 | 292 | } |
405 | | |
406 | | static void save_pack_keys(const GC_Chat *_Nonnull chat, Bin_Pack *_Nonnull bp) |
407 | 445 | { |
408 | 445 | bin_pack_array(bp, 4); |
409 | | |
410 | 445 | pack_extended_public_key(&chat->chat_public_key, bp); // 1 |
411 | 445 | pack_extended_secret_key(&chat->chat_secret_key, bp); // 2 |
412 | 445 | pack_extended_public_key(&chat->self_public_key, bp); // 3 |
413 | 445 | pack_extended_secret_key(&chat->self_secret_key, bp); // 4 |
414 | 445 | } |
415 | | |
416 | | static void save_pack_self_info(const GC_Chat *_Nonnull chat, Bin_Pack *_Nonnull bp) |
417 | 445 | { |
418 | 445 | bin_pack_array(bp, 4); |
419 | | |
420 | 445 | GC_Peer *self = &chat->group[0]; |
421 | | |
422 | 445 | if (self->nick_length > MAX_GC_NICK_SIZE) { |
423 | 0 | LOGGER_ERROR(chat->log, "self_nick is too big (%u). Truncating to %d", self->nick_length, MAX_GC_NICK_SIZE); |
424 | 0 | self->nick_length = MAX_GC_NICK_SIZE; |
425 | 0 | } |
426 | | |
427 | 445 | bin_pack_u16(bp, self->nick_length); // 1 |
428 | 445 | bin_pack_u08(bp, (uint8_t)self->role); // 2 |
429 | 445 | bin_pack_u08(bp, self->status); // 3 |
430 | 445 | bin_pack_bin(bp, self->nick, self->nick_length); // 4 |
431 | 445 | } |
432 | | |
433 | | static void save_pack_saved_peers(const GC_Chat *_Nonnull chat, Bin_Pack *_Nonnull bp) |
434 | 445 | { |
435 | 445 | bin_pack_array(bp, 2); |
436 | | |
437 | 445 | uint8_t *saved_peers = (uint8_t *)mem_balloc(chat->mem, GC_MAX_SAVED_PEERS * GC_SAVED_PEER_SIZE); |
438 | | |
439 | | // we can still recover without the saved peers list |
440 | 445 | if (saved_peers == nullptr) { |
441 | 4 | bin_pack_u16(bp, 0); // 1 |
442 | 4 | bin_pack_nil(bp); // 2 |
443 | 4 | LOGGER_ERROR(chat->log, "Failed to allocate memory for saved peers list"); |
444 | 4 | return; |
445 | 4 | } |
446 | | |
447 | 441 | uint16_t packed_size = 0; |
448 | 441 | const int count = pack_gc_saved_peers(chat, saved_peers, GC_MAX_SAVED_PEERS * GC_SAVED_PEER_SIZE, &packed_size); |
449 | | |
450 | 441 | if (count < 0) { |
451 | 0 | LOGGER_ERROR(chat->log, "Failed to pack saved peers"); |
452 | 0 | } |
453 | | |
454 | 441 | bin_pack_u16(bp, packed_size); // 1 |
455 | | |
456 | 441 | if (packed_size == 0) { |
457 | 72 | bin_pack_nil(bp); // 2 |
458 | 72 | mem_delete(chat->mem, saved_peers); |
459 | 72 | return; |
460 | 72 | } |
461 | | |
462 | 369 | bin_pack_bin(bp, saved_peers, packed_size); // 2 |
463 | | |
464 | 369 | mem_delete(chat->mem, saved_peers); |
465 | 369 | } |
466 | | |
467 | | void gc_save_pack_group(const GC_Chat *chat, Bin_Pack *bp) |
468 | 2.00k | { |
469 | 2.00k | if (chat->numpeers == 0) { |
470 | 1.56k | LOGGER_ERROR(chat->log, "Failed to pack group: numpeers is 0"); |
471 | 1.56k | return; |
472 | 1.56k | } |
473 | | |
474 | 445 | bin_pack_array(bp, 7); |
475 | | |
476 | 445 | save_pack_state_values(chat, bp); // 1 |
477 | 445 | save_pack_state_bin(chat, bp); // 2 |
478 | 445 | save_pack_topic_info(chat, bp); // 3 |
479 | 445 | save_pack_mod_list(chat, bp); // 4 |
480 | 445 | save_pack_keys(chat, bp); // 5 |
481 | 445 | save_pack_self_info(chat, bp); // 6 |
482 | 445 | save_pack_saved_peers(chat, bp); // 7 |
483 | 445 | } |